Skip to content

Writing tests

Dmitry Degtyarev edited this page Oct 1, 2021 · 28 revisions

Test arena and test parent

The testing suite creates a domain object "test-arena" before each test. Everything that happens inside the test must happen in this object. For example, if you need to create an object for testing, create it inside test arena. After each test, the test arena is deleted and recreated.

The suite also has a parent widget which should be the parent of all widgets created inside a test. Same thing as the test arena, created before each test and deleted after each test.

Finding widgets

Simulating input events is the easiest way to write tests and good for prototyping BUT it is not robust. When the tested code changes locations of widgets or rearranges things even slightly the test will break and you will have to update it. For this reason, try to avoid simulated input events in final versions of tests by using functions such as click() and setText() directly on widgets. To use these functions you will need to find widgets.

widget->findChild<WidgetClass *>() - to get children of some widget. For example when you've called some f-n that opens a dialog, but the f-n doesn't return pointer to dialog. You can also use setObjectName() in the app code to give names to widgets for cases where there are multiple widget children of same type and you need to differentiate them. Then pass that name to findChild().

The alternative of creating a getter for a specific widget in the tested code is also available but the findChild() method should be preferred because it doesn't require modifying tested code just for tests.

Misc

QVERIFY(QTest::qWaitForWindowExposed(find_dialog, 1000)); - this must be called after a new widget becomes active, for example after a dialog is opened. If you don't call this, then code afterwards may perform keyboard clicks or other actions before the widget gets focus.

QVERIFY() - this works weirdly because it is supposed to end the test if it fails to verify, but it only does this if it is called within the scope of the test. Therefore, you shouldn't call QVERIFY(), QVERIFY2() OR QCOMPARE() in local functions or other member functions, only in the main test function.

QCOMPARE() - this is strictly better than QVERIFY() except for checking straight boolean conditions. If you're checking equality between strings, lists or other comparable data types always use QCOMPARE(), not QVERIFY(). This will print the values in case of error which is very useful for debugging.

Inheritance doesn't work with tests. You can't make a base class with some test cases, have to do composition.

Dialogs and exec()

Dialogs opened using exec() block the execution loop, which makes testing impossible (unless you go crazy and do testing through threads, don't). In general, the codebase should only use open() but if an exec() call snuck in somehow, you will need to first rework that code to use open() and then you'll be able to test it.

Clone this wiki locally