QGISプラグインの自動テストを作成していますか?


16

Pythonで作成されたQGISプラグインの自動テストを作成するためのアドバイスを探しています。

過去にPyUnit(unittestモジュール)を使用してPythonスクリプトのテストを作成しましたが、GUIを使用するアプリケーションではテストを実行したことがありません。PyQt4.QTestを使用してQtウィジェットでユニットテストを行う方法を説明するページを見つけました(http://www.voom.net/pyqt-qtest-example) QGIS内から実行するように設計されたウィジェットを使用します。

PyQGISドキュメントの「テスト」のセクションは特にありません。

私がこれまでに持っているものは:

  • 分離されたモジュールまたは関数でデータの実際の処理を維持し、それらのユニットテストを記述します。
  • QTestを使用してUIの基本的なテストを実行します。
  • QGIS内からプラグインを使用するときにすべてが一緒に保持されるように。

もっと良い方法はありますか?

回答:


11

QGISプラグインのテスト機能(特に、OPが強調するように、QGIS環境内での統合テストの問題)は、最近大幅に改善されました。したがって、このアップデートが現代の読者だけでなくOPにも役立つことを願っています。

Boundlessは、2016年7月に、QGISプラグインのテストの自動化に真剣に取り組む人向けの必読記事を公開しました。Pythonプラグイン用のQGIS継続的統合テスト環境。それは彼らが使用するアプローチとツールを説明しています-それらはすべてオープンソースです。主な側面は次のとおりです。

  • QGIS環境のテストを自動化できる特別なQGISプラグインテスター
  • コンテナーベース環境でのさまざまなQGISバージョン/構成に対するテストを可能にする、Docker QGISイメージの使用
  • 特別ドッカーQGIS画像 QGIS自体をテストするために使用されるが、これ-呼び出すことによってはqgis_testrunner.sh、プラグインにユニットテストを実行するために使用することができます
  • 継続的な統合のためのTravis CIの使用。つまり、新しいコードがコミットされるたびに完全なテストスイートが実行されます。

Travis CI / dockerに精通している場合、比較的簡単に設定できます。以下の4つの手順を説明し、この方法でセットアップされた独自のプラグインの2つの例を提供します。

  1. QGISテスト環境でDockerイメージをプルして実行します
  2. qgis_setup.sh NameOfYourPluginを実行してプラグインをインストールし、テストランナー用にQGISを準備します
  3. オプションで、プラグインのビルドに必要なすべての操作を実行します
  4. Docker内でテストランナーを実行し、 qgis_testrunner.sh

あなたはベストプラクティスを求めましたが、今日の時点で私は確かにこれを検討します。QGISドキュメントにはまだプラグインテストに関する専用のセクションがありません(これはまもなく変更されると思います)が、「すべてが一緒になっていることを祈る」アプローチは確かにもはや唯一の選択肢ではありません。


4
無限はもはやありません。誰かがそのコンテンツを保存しましたか?
ペドロカマルゴ

8

これはunittestスタンドアロンPythonアプリケーションにロードされたPythonプラグインのテストに使用できるようです

qgis.core.ifaceはスタンドアロンアプリケーションからは使用できないため、与えられた引数を受け入れ、それ以外は何もしない関数を返すダミーインスタンスを作成しました。つまり、次のような呼び出しはself.iface.addToolBarIcon(self.action)エラーをスローしません。

以下の例では、プラグインをロードします。プラグインmypluginには、マップレイヤーレジストリから取得したレイヤー名を含むいくつかのドロップダウンメニューがあります。テストでは、メニューが正しく読み込まれ、相互作用できるかどうかを確認します。これがプラグインをロードする最良の方法であるかどうかはわかりませんが、うまくいくようです。

mypluginウィジェット

#!/usr/bin/env python

import unittest

import os
import sys

# configure python to play nicely with qgis
osgeo4w_root = r'C:/OSGeo4W'
os.environ['PATH'] = '{}/bin{}{}'.format(osgeo4w_root, os.pathsep, os.environ['PATH'])
sys.path.insert(0, '{}/apps/qgis/python'.format(osgeo4w_root))
sys.path.insert(1, '{}/apps/python27/lib/site-packages'.format(osgeo4w_root))

# import Qt
from PyQt4 import QtCore, QtGui, QtTest
from PyQt4.QtCore import Qt

# import PyQGIS
from qgis.core import *
from qgis.gui import *

# disable debug messages
os.environ['QGIS_DEBUG'] = '-1'

def setUpModule():
    # load qgis providers
    QgsApplication.setPrefixPath('{}/apps/qgis'.format(osgeo4w_root), True)
    QgsApplication.initQgis()

    globals()['shapefile_path'] = 'D:/MasterMap.shp'

# FIXME: this seems to throw errors
#def tearDownModule():
#    QgsApplication.exitQgis()

# dummy instance to replace qgis.utils.iface
class QgisInterfaceDummy(object):
    def __getattr__(self, name):
        # return an function that accepts any arguments and does nothing
        def dummy(*args, **kwargs):
            return None
        return dummy

class ExamplePluginTest(unittest.TestCase):
    def setUp(self):
        # create a new application instance
        self.app = app = QtGui.QApplication(sys.argv)

        # create a map canvas widget
        self.canvas = canvas = QgsMapCanvas()
        canvas.setCanvasColor(QtGui.QColor('white'))
        canvas.enableAntiAliasing(True)

        # load a shapefile
        layer = QgsVectorLayer(shapefile_path, 'MasterMap', 'ogr')

        # add the layer to the canvas and zoom to it
        QgsMapLayerRegistry.instance().addMapLayer(layer)
        canvas.setLayerSet([QgsMapCanvasLayer(layer)])
        canvas.setExtent(layer.extent())

        # display the map canvas widget
        #canvas.show()

        iface = QgisInterfaceDummy()

        # import the plugin to be tested
        import myplugin
        self.plugin = myplugin.classFactory(iface)
        self.plugin.initGui()
        self.dlg = self.plugin.dlg
        #self.dlg.show()

    def test_populated(self):
        '''Are the combo boxes populated correctly?'''
        self.assertEqual(self.dlg.ui.comboBox_raster.currentText(), '')
        self.assertEqual(self.dlg.ui.comboBox_vector.currentText(), 'MasterMap')
        self.assertEqual(self.dlg.ui.comboBox_all1.currentText(), '')
        self.dlg.ui.comboBox_all1.setCurrentIndex(1)
        self.assertEqual(self.dlg.ui.comboBox_all1.currentText(), 'MasterMap')

    def test_dlg_name(self):
        self.assertEqual(self.dlg.windowTitle(), 'Testing')

    def test_click_widget(self):
        '''The OK button should close the dialog'''
        self.dlg.show()
        self.assertEqual(self.dlg.isVisible(), True)
        okWidget = self.dlg.ui.buttonBox.button(self.dlg.ui.buttonBox.Ok)
        QtTest.QTest.mouseClick(okWidget, Qt.LeftButton)
        self.assertEqual(self.dlg.isVisible(), False)

    def tearDown(self):
        self.plugin.unload()
        del(self.plugin)
        del(self.app) # do not forget this

if __name__ == "__main__":
    unittest.main()

4
それ以来、この回答に基づいて記事を書いています:snorf.net/blog/2014/01/04/…– Snorfalorpagus 14
1


-1

これにより、QTestおよびunittest http://www.voom.net/pyqt-qtest-exampleを使用してPyQt GUIをテストできます


1
それは質問でリンクされている「このページ」です(明らかにあまり明確ではありません)。私の問題は、QGISでレイヤーが配置されたコンボボックスなどで実行するように設計されたインターフェイスをどのようにテストするかです。
Snorfalorpagus
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.