Piszesz automatyczne testy wtyczek QGIS?

16

Szukam porady na temat pisania automatycznych testów wtyczek QGIS napisanych w Pythonie.

Testy skryptów Pythona pisałem w przeszłości przy użyciu PyUnit ( unittestmoduł), ale nigdy nie robiłem tego dla aplikacji z GUI. Znalazłem stronę opisującą sposób korzystania z PyQt4.QTest do przeprowadzania testów jednostkowych w widżetach Qt ( http://www.voom.net/pyqt-qtest-example ), ale staram się zobaczyć, jak mogę tego użyć z widżetem, który został zaprojektowany do działania z poziomu QGIS.

Sekcja „Testowanie” w dokumentacji PyQGIS jest wyraźnie nieobecna.

Do tej pory mam:

  • Zachowaj faktyczne przetwarzanie danych w izolowanych modułach lub funkcjach i napisz dla nich testy jednostkowe;
  • Przeprowadź podstawowe testy interfejsu użytkownika za pomocą QTest;
  • Módlcie się, aby wszystko trzymało się razem podczas korzystania z wtyczki z QGIS.

Czy jest lepszy sposób?

Snorfalorpagus
źródło

Odpowiedzi:

11

Możliwości testowania wtyczek QGIS (szczególnie kwestia testowania integracji w środowisku QGIS, jak podkreśla OP) ostatnio znacznie się poprawiły. Dlatego mam nadzieję, że ta aktualizacja pomoże współczesnym czytelnikom, a także OP.

Boundless opublikował artykuł do przeczytania w lipcu 2016 r. Dla każdego, kto poważnie myśli o automatyzacji testowania wtyczek QGIS, zatytułowany; Środowisko testowania ciągłej integracji QGIS dla wtyczek Python . Opisuje podejście i używane przez nich narzędzia - z których wszystkie są oprogramowaniem typu open source. Kluczowe aspekty to:

  • Ich specjalny tester wtyczek QGIS, który może zautomatyzować testy w środowisku QGIS
  • Zastosowanie obrazów dokowanych QGIS, umożliwiających testowanie różnych wersji / konfiguracji QGIS w środowisku kontenerowym
  • Specjalny doker QGIS obraz , który jest używany do testowania samego QGIS, ale który - powołując qgis_testrunner.shmoże być używany do uruchamiania testów jednostkowych na wtyczce
  • Wykorzystanie Travis CI do ciągłej integracji - tzn. Pełny zestaw testów jest uruchamiany przy każdym nowym zatwierdzeniu kodu

Jeśli znasz Travis CI / docker, konfiguracja powinna być stosunkowo łatwa. Opisują 4 następujące kroki i podają 2 przykłady własnych wtyczek skonfigurowanych w ten sposób.

  1. Wyciągnij obraz Docker ze środowiskiem testowym QGIS i uruchom go
  2. Uruchom qgis_setup.sh NameOfYourPlugin, aby zainstalować wtyczkę i przygotować QGIS dla testera
  3. Opcjonalnie wykonaj wszystkie operacje potrzebne do zbudowania wtyczki
  4. Uruchom testera w Dockerze, wywołując polecenie qgis_testrunner.sh

Poprosiłeś o najlepszą praktykę i na pewno z pewnością to rozważę. Dokumenty QGIS wciąż nie mają wydzielonej sekcji poświęconej testowaniu wtyczek (spodziewam się, że wkrótce się to zmieni), ale podejście „Módlcie się, aby wszystko się trzymało” nie było już jedyną opcją.

MatzFan
źródło
4
Nie ma już granic. Czy ktoś zapisał tę treść?
Pedro Camargo,
8

Wygląda na to, że można to wykorzystać unittestdo testowania wtyczek Python załadowanych do samodzielnej aplikacji Python .

Funkcja qgis.core.iface nie jest dostępna w samodzielnych aplikacjach, więc napisałem fikcyjną instancję, która zwraca funkcję, która akceptuje wszelkie podane argumenty i nic więcej nie robi. Oznacza to, że połączenia typu „ self.iface.addToolBarIcon(self.action)nie rzucaj błędów”.

Poniższy przykład ładuje wtyczkę z mypluginkilkoma rozwijanymi menu z nazwami warstw pobranymi z rejestru warstw mapy. Testy sprawdzają, czy menu zostały poprawnie wypełnione i można z nimi korzystać. Nie jestem pewien, czy to najlepszy sposób na załadowanie wtyczki, ale wydaje się, że działa.

widget 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()
Snorfalorpagus
źródło
4
Od tamtej pory napisałem artykuł na podstawie tej odpowiedzi tutaj: snorf.net/blog/2014/01/04/…
Snorfalorpagus
3

Złożyłem również interfejs DummyInterface, który umożliwia samodzielne testowanie wtyczek QGIS. Po przeczytaniu bloga Snorfalorpagus, sprawdź moją odpowiedź tutaj .

Aby znaleźć prawdziwy przykład, w jaki sposób testowałem (ed) wtyczki QGIS, odwiedź ten projekt github na https://github.com/UdK-VPT/Open_eQuarter/tree/master/mole i zajrzyj do testów - pakiet.

Kim
źródło
-1

Może to pomóc: Przetestuj GUI PyQt za pomocą QTest i unittest http://www.voom.net/pyqt-qtest-example

Stefan
źródło
1
To jest „ta strona”, do której prowadzi link w pytaniu (co prawda nie jest zbyt jasne). Mój problem polega na tym, jak przetestować interfejs zaprojektowany do działania z takimi elementami jak pola kombi wypełnione warstwami w QGIS.
Snorfalorpagus,