Написання автоматизованих тестів для плагінів QGIS?


16

Я шукаю поради щодо написання автоматичних тестів для QGIS плагінів, написаних на Python.

Я раніше писав тести для сценаріїв Python, використовуючи PyUnit ( unittestмодуль), але ніколи цього не робив для програми із графічним інтерфейсом. Я знайшов сторінку, що описує, як використовувати PyQt4.QTest, щоб робити одиничні тести на віджети Qt ( http://www.voom.net/pyqt-qtest-example ), але я намагаюся зрозуміти, як я можу це використовувати з віджетом, призначеним для роботи в межах QGIS.

У розділі "Тестування" в документації PyQGIS помітно відсутній.

Я маю поки що:

  • Зберігати фактичну обробку даних в ізольованих модулях чи функціях та записувати одиничні тести для них;
  • Провести базове тестування інтерфейсу користувача за допомогою QTest;
  • Моліться, щоб це все було разом, коли ви використовуєте плагін з QGIS.

Чи є кращий спосіб?

Відповіді:


11

Можливості тестування плагінів QGIS (особливо питання інтеграційного тестування в середовищі QGIS, як підкреслює ОП) останнім часом значно покращилися. Тому я сподіваюся, що це оновлення допоможе сучасним читачам, а також ОП.

Blessless опублікував обов'язкову для читання статтю в липні 2016 року для тих, хто серйозно ставиться до автоматизації тестування плагінів QGIS під назвою; Середовище тестування QGIS для безперервної інтеграції для плагінів Python . Він описує підхід та інструменти, якими вони користуються - всі вони є відкритим кодом. Основні аспекти:

  • Їх спеціальний тестер QGIS плагінів, який може автоматизувати тести всередині QGIS
  • Використання докерських QGIS-зображень, що дозволяють протестувати різні версії / конфігурації QGIS в середовищі-контейнері
  • Спеціальний докер QGIS зображення , яке використовується для тестування самого QGIS, але - при призові qgis_testrunner.shможе бути використано для запуску модульних тестів на плагіні
  • Використання Travis CI для постійної інтеграції - тобто повний тестовий набір запускається з кожним новим кодом

Якщо ви знайомі з Travis CI / docker, налаштувати його слід досить просто. Вони описують наступні 4 етапи та надають 2 приклади власних плагінів, створених таким чином.

  1. Витягніть зображення Docker за допомогою тестового середовища QGIS і запустіть його
  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

3

Я також склав DummyInterface разом, що дозволяє вам протестувати плагіни QGIS окремо. Прочитавши блог Snorfalorpagus, перегляньте мою відповідь тут .

Щоб знайти приклад із реального життя про те, як я тестую (редагую) QGIS-плагіни, відвідайте цей проект github за адресою https://github.com/UdK-VPT/Open_eQuarter/tree/master/mole та ознайомтеся з тестами - пакет.


-1

Це може допомогти: Тестуйте графічні інтерфейси PyQt за допомогою QTest та unittest http://www.voom.net/pyqt-qtest-example


1
Це "ця сторінка", пов'язана з питанням (правда, не надто чітко). Моя проблема полягає в тому, як я перевіряю інтерфейс, призначений для роботи з такими речами, як комбіновані поля, заповнені шарами в QGIS.
Snorfalorpagus
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.