Writing unit tests with PyTest

If the Tests can’t be found then try the steps written in How to run tests from PyCharm

Import the class that needs to be tested

When wanting to test a class, it first needs to be imported into the test file. It is also useful to import pytest and Qt. The example import statements can be found in the figure below:

import pytest
from PySides6.QtCore import Qt
from interactive_topic_modeling.display.model_params_display import ModelParamsDisplay

Initialize object to perform tests on

Unit tests that will be performed (usually) make use of a fixture. If tests do not have to be run on the same object, fixtures are not strictly necessary. For this example, fixture code will be shown for both non-qt as well as qt objects. For the UI it is useful for all test to use the same instance of an object, and thus use a fixture. If you repeatedly have to create instances of the non-Qt object, fixtures can also be useful. A fixture an initial condition of a piece of code that will be tested. For example, if we want to test the model_parameter_display, we will first setup an initial parameter_display tagged as a fixture. This fixture will then be used in further unit tests. Fixtures would only have to written once for all tests for that object. For non-Qt objects, the fixture can be made like in the figure below, but to reiterate, with non frontend objects, this in not necessary as new instances can be made every test. If you don’t want to make a new instance every test, a fixture can be used.

@pytest.fixture
def gensim_lda_model():
    # Prepare sample term lists and other required parameters
    term_lists = [["word1", "word2", "word3"], ["word4", "word5", "word6"]l]
    num_topics = 2
    
    # Instantiate the model
    model = GensimLdaModel(term_lists, num_topics)
    
    return model 

Qt objects work a bit differently. When working with testing UI, we will most likely use bots to simulate the user interacting with the UI. The widget that we want to test needs to be linked to the bot. The widget also needs to be returned so it can be used in tests. An example of a Qt fixture is visible in the figure below:

@pytest.fixture

def parameter_display(qtbot):
    # Create and return your parameter_display object here
    parameter_display = ModelParamsDisplay()
    # Attach the widget to the bot that will simulate the user
    qtbot.addWidget(parameter_display)
    return parameter_display

Write unit tests

Now that the objects have been made, they can be tested. The goal with a unit test is that one ‘unit’ of code is tested at a time. This could for example be the initialization of the object, the functionality of the object or the interaction of the object. Every test should be a ‘def’, where the parameter is the instance of the object that was made in the fixture. Some kind of assertion will be made in the test, determining if the code does what it has to do or not. An example of a unit test can be seen in the figure below:

def test_model_params_display_user_interaction(parameter_display, qtbot):
    # Set some kind of input
    parameter_display.topic_input.setText("10")
    # Have the user simulate the action of validating the input
    qtbot.keyPress(parameter_display.topic_input, Qt.Key_Return)
    # See if the result is the same as expected
    assert parameter_display.fetch_topic_num() == 10

These example tests can also be checked in the repository. The Qt test can be seen in example_test2. The non Qt test can be seen in example_test3