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

16

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

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

Раздел «Тестирование» в документации PyQGIS отсутствует.

То, что я до сих пор это:

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

Есть ли способ лучше?

Snorfalorpagus
источник

Ответы:

11

Возможности для тестирования плагинов QGIS (особенно вопрос тестирования интеграции в среде QGIS, как подчеркивает OP) в последнее время значительно улучшились. Поэтому я надеюсь, что это обновление поможет современным читателям, а также ОП.

В июле 2016 года Boundless опубликовал обязательную для прочтения статью для всех, кто серьезно относится к автоматизации тестирования подключаемых модулей 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 до сих пор нет отдельного раздела по тестированию плагинов (я ожидаю, что это скоро изменится), но подход «Молитесь, чтобы все вместе» больше не является единственным вариантом.

MatzFan
источник
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()
Snorfalorpagus
источник
4
С тех пор я написал статью на основе этого ответа здесь: snorf.net/blog/2014/01/04/…
Snorfalorpagus
3

Я также собрал DummyInterface, который позволяет вам тестировать плагины QGIS автономно. После прочтения блога Snorfalorpagus, проверьте мой ответ здесь .

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

Ким
источник
-1

Это может помочь: протестировать PyQt GUI с QTest и unittest http://www.voom.net/pyqt-qtest-example

Стефан
источник
1
Это ссылка на «эту страницу» в вопросе (правда, не слишком ясно). Моя проблема в том, как мне протестировать интерфейс, который предназначен для работы с такими вещами, как комбинированные блоки, заполненные слоями в QGIS.
Снорфалорпаг