😺

PySdie、PyQtをpytestする際に、pytest-qtを使う

3 min read

はじめに

以前、 「PySide & Pytest での テスト駆動開発 スタートアップ」にて、PySide + pytestする方法をまとめたが、以下のようなものをやっていた。

まずconftest.pyで、fixtureとして次を登録し、

conftest.py
@pytest.fixture
def base_window():
    app = QApplication()
    mainWindow = QMainWindow()
    mainWindow.show()
    return mainWindow

テストで、それを利用して、ウィジェットのスモークテストを行う

test_widget.py
def test_addWidget(base_widget):
    from MyCustomPySideLib,QCustomWidgets import QMyWidget
    tagWidget = QMyWidget()
    base_widget.setCentralWidget(tagWidget)

ということをしていたが、どうやら別の方法を提供している人もいるらしいという事で調べてみた。

pytest-qtの導入

pytest-qtで、こういったところをシミュレートできる。
pytest-qtは、pytestのプラグインとして扱われる。

インストールは、pipで行える。

pip install pytest-qt

pytest-qtを使って書く

pytest-qtを使ってはじめのテストコードを書き直すとこうなる。

Widgetの追加テスト

test_widget.py
def test_addWidget(qtbot):
    from MyCustomPySideLib,QCustomWidgets import QMyWidget
    myWidget = QMyWidget()
    qtbot.addWidget(myWidget)

ここで引数として使用されているqtbotが、pytest-qtをインストールすることによって使用できる用になるfixtureとなる。
このqtbotは、人の操作によるテストに加えて、Qt側のもろもろのテストとエラーに対するキャプチャ機能を備えている。

例えば、エラーをキャプチャする際にはこんなかんじ。

with qtbot.capture_exceptions() as exceptions:
    qtbot.click(button)

シグナル待ちを含めたテスト

長時間処理を行う場合でのシグナル待ちや別スレッド処理中のシグナル待ちなどを含めたテストを書くこともできる。

def test_long_computation(qtbot):
    app = Application()

    with qtbot.waitSignal(app.worker.finished, timeout=10000) as blocker:
        blocker.connect(app.worker.failed)
        app.worker.start()
	
    assert_application_results(app)

特定の状態待ちテスト

例えば、「ウィンドウのステータステキストが『Please input a number』になってなっているか」といったテストを書きたい場合、次のように書ける。

def test_validate(qtbot):
    window = MyWindow()
    window.edit.setText("not a number")
    window.edit.setFocus()

    def check_label():
        assert window.status.text() == "Please input a number"

    qtbot.waitUntil(check_label)

出力はこんな感じ

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    def check_label():
>       assert window.status.text() == "Please input a number"
E       AssertionError: assert 'OK' == 'Please input a number'
E         - OK
E         + Please input a number
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>       qtbot.waitUntil(check_label)
E       pytestqt.exceptions.TimeoutError: waitUntil timed out in 1000 miliseconds

まとめ

PySideのふるまいに関して、それぞれまじめにテストを書くという手もあるが、エラーキャプチャ周りや、スレッド分けしたものを追っていく、またはその方法を作るのは大変なので、このプラグインを使用することによって、その手間を省いてくれるのはありがたい。
ドキュメントを読んでいくと、まだまだ機能がある様なので、いろいろ試してみての恩恵具合を見てみたいところ。