Menulis tes otomatis untuk plugin QGIS?

16

Saya mencari saran untuk menulis tes otomatis untuk plugin QGIS yang ditulis dengan Python.

Saya telah menulis tes untuk skrip Python di masa lalu menggunakan PyUnit ( unittestmodul), tetapi belum pernah melakukannya untuk aplikasi dengan GUI. Saya telah menemukan halaman yang menjelaskan cara menggunakan PyQt4.QTest untuk melakukan pengujian unit pada widget Qt ( http://www.voom.net/pyqt-qtest-example ), tapi saya berjuang untuk melihat bagaimana saya bisa menggunakan ini dengan widget yang telah dirancang untuk dijalankan dari dalam QGIS.

Bagian "Pengujian" dalam dokumentasi PyQGIS khususnya tidak ada.

Sejauh ini yang saya miliki adalah:

  • Pertahankan pemrosesan data aktual dalam modul atau fungsi yang terisolasi, dan tulis unit test untuk itu;
  • Lakukan pengujian dasar UI menggunakan QTest;
  • Berdoalah agar semuanya dapat bertahan bersama saat menggunakan plugin dari dalam QGIS.

Apakah ada cara yang lebih baik?

Snorfalorpagus
sumber

Jawaban:

11

Kemampuan untuk menguji plugin QGIS (terutama pertanyaan tentang pengujian integrasi, dalam lingkungan QGIS, seperti yang disoroti OP) telah meningkat pesat akhir-akhir ini. Oleh karena itu saya berharap pembaruan ini akan membantu pembaca kontemporer, serta OP.

Boundless menerbitkan artikel yang harus dibaca pada Juli 2016 untuk siapa pun yang serius mengotomatisasi pengujian plugin QGIS yang berjudul; Lingkungan Pengujian Integrasi Berkelanjutan QGIS untuk Plugin Python . Ini menjelaskan pendekatan dan alat yang mereka gunakan - yang semuanya open source. Aspek kuncinya adalah: -

  • Penguji plugin QGIS khusus mereka yang dapat mengotomatisasi pengujian di dalam lingkungan QGIS
  • Penggunaan gambar QGIS buruh pelabuhan , memungkinkan pengujian terhadap berbagai versi / konfigurasi QGIS dalam lingkungan berbasis wadah
  • Sebuah gambar khusus buruh pelabuhan QGIS , yang digunakan untuk pengujian QGIS itu sendiri, tetapi yang - oleh memohon qgis_testrunner.shdapat digunakan untuk menjalankan unit test pada plugin
  • Penggunaan Travis CI untuk integrasi berkesinambungan - yaitu suite uji lengkap dijalankan dengan setiap komit kode baru

Jika Anda terbiasa dengan Travis CI / buruh pelabuhan itu seharusnya relatif mudah diatur. Mereka menggambarkan 4 langkah berikut dan memberikan 2 contoh plugin mereka sendiri yang diatur dengan cara ini.

  1. Tarik gambar Docker dengan lingkungan pengujian QGIS dan jalankan
  2. Jalankan qgis_setup.sh NameOfYourPlugin Anda untuk menginstal plugin dan menyiapkan QGIS untuk pelari uji
  3. Secara opsional, lakukan semua operasi yang diperlukan untuk membangun plugin Anda
  4. Jalankan test runner di dalam Docker dengan memanggil qgis_testrunner.sh

Anda meminta praktik terbaik & hari ini saya pasti akan mempertimbangkannya. Dokumentasi QGIS masih belum memiliki bagian khusus tentang pengujian plugin (saya berharap ini akan segera berubah) tetapi pendekatan "Doakan agar semuanya tetap bersama" jelas bukan satu-satunya pilihan.

MatzFan
sumber
4
Tanpa batas tidak ada lagi. Adakah yang menyimpan konten itu?
Pedro Camargo
8

Sepertinya ini dimungkinkan untuk digunakan unittestuntuk menguji plugin Python yang dimuat ke dalam aplikasi Python yang berdiri sendiri .

qgis.core.iface tidak tersedia dari aplikasi mandiri, jadi saya telah menulis contoh dummy yang mengembalikan fungsi yang akan menerima argumen yang diberikan padanya dan tidak melakukan apa pun. Ini berarti bahwa panggilan seperti self.iface.addToolBarIcon(self.action)jangan melempar kesalahan.

Contoh di bawah ini memuat sebuah plugin myplugin, yang memiliki beberapa menu drop down dengan nama layer yang diambil dari registry layer peta. Tes memeriksa untuk melihat apakah menu telah diisi dengan benar, dan dapat berinteraksi dengan. Saya tidak yakin apakah ini cara terbaik untuk memuat plugin, tetapi tampaknya berhasil.

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
sumber
4
Sejak itu saya telah menulis sebuah artikel berdasarkan jawaban ini di sini: snorf.net/blog/2014/01/04/…
Snorfalorpagus
3

Saya juga telah menyatukan DummyInterface, yang memungkinkan Anda untuk menguji plugin QGIS mandiri. Setelah membaca blog Snorfalorpagus, lihat jawaban saya di sini .

Untuk menemukan contoh nyata, tentang cara saya menguji (ed) QGIS-plugins, kunjungi proyek github ini di https://github.com/UdK-VPT/Open_eQuarter/tree/master/mole dan lihatlah dalam tes - paket.

Kim
sumber
-1

Ini bisa membantu: Menguji GUI PyQt dengan QTest dan http://www.ittoom.net/pyqt-qtest-example yang belum dikirim

Stefan
sumber
1
Itulah "halaman ini" yang ditautkan dalam pertanyaan (diakui tidak terlalu jelas). Masalah saya adalah, bagaimana cara menguji antarmuka yang dirancang untuk dijalankan dengan hal-hal seperti kotak kombo yang diisi dengan lapisan dalam QGIS.
Snorfalorpagus