Parametrize tests in Python

This article explains how to parametrize tests in Python.

What is test parametrization?

Let’s say we want to execute the same test scenario using different parameters to cover different edges or use cases. We can use parametrization to pass this set of different parameter to the same code block and not duplicate code to cover all scenarios.

Note:

The parametrization of test functions happens at collection time. It is a good idea to set up expensive resources like DB connections or subprocess only when the actual test is run.

Following are the main benefits of parametrization are:

  • By externalizing changing parts of tests as parameters, it is easier to manage complex test cases.
  • You can automatically run multiple variations of each test. If a parameter has multiple values provided, that will cause several runs of each test for each variation of the parameter.
  • Parameters allow sharing information between multiple test cases. Sharing is easily achieved by defining parameter values in the test sets. This effectively and automatically shares these values in all test cases that are part of the test set.

To Achieve our goal. we will use PyTest.

PyTest

PyTest is a Python open source library for testing applications running unit, functional, and acceptance tests. It is similar to JUnit and TestNG (but not the same).

Requirements

We will need to implement PyTest Marks to note in our framework that the current test is parametrized. PyTest Marks are similar to TestNG annotations and follow the following syntax:

Copy
@pytest.mark.<name>.

You can check the article for PyTest marks for more details. The mark we will use in our scenario is: @pytest.mark.parametrize

As soon as the framework executes a parametrized test, it will generate as many tests as the arguments we are providing. For example, if we have 4 different scenarios parametrized in a single test, the framework will run this test 4 times, each time using the arguments for a different scenario, and in the result, we will see 4 different test results: one for each scenario.

Implement the parametrization in the test code

Copy
Import pytest

call_par = ['4;4;True',
           '2;3;False',
           '5;5;True']

@pytest.mark.parametrize('call_par', call_par)
def test_math(call_par):
    """    developer: yana.todorova
    """    (old_value, new_value, equals) = call_par.split(';')
    print("it is " + equals + " that " + old_value + " is equal to " + new_value)
    assert old_value == new_value


if __name__ == "__main__":
    pytest.main([__file__, "-k", "test_", "-v", "-s"])

Let's read our simple code now. First, we have a list call_par that includes 3 strings, which represent our 3 scenarios with their arguments:

  • Scenario 1 arguments: 4; 4 and True

  • Scenario 2 arguments: 2; 3 and False

  • Scenario 3 arguments: 5; 5 and True

Our simple code will compare each scenario's first and second argument and check if they are equal. We will print a message that states if they are equal or not. (This is not automated print that really checks if they are equal. We only use it here so that we can use the third argument somewhere.)

Moving forward, we have our PyTest mark :

Copy
@pytest.mark.parametrize('call_par', call_par)

It passes the name of our list and assigns it to our test bellow. Next, we create our test, passing the same list to it.

Copy
def test_math(call_par):

Inside our test, we now have to pull out our arguments from the scenario string sent to the test. We do this by splitting the string each time we see ; and assigning each argument to a variable. This way, we will end up with 3 different arguments (or as many as you have set), as follows:

Copy
(old_value, new_value, equals) = call_par.split(';')

where:

The left side creates variables and the right side assigns the arguments that will be generated from the string split. In our case for Scenario 2, we will end up  with the following:

  • old_value = 2

  • new_value = 3

  • equals = False

Note:
  • All arguments are currently of type string.

  • Aach scenario must have the same number of arguments. It is not possible to have scenario 1 with a string that includes 2 arguments and scenario 2 with a string that includes 3 arguments.

After running the test, we need to call PyTest to execute our script by using the following lines:

Copy
if __name__ == "__main__":
    pytest.main([__file__, "-k", "test_", "-v", "-s"])

where:

  • -k runs the test by keyword, in this case, test_.

    By default, PyTest only identifies file names starting with test_ or ending with _test as test files. If the test is named something else, you can use –k to define this.

  • -v increases the verbosity.

  • -s  enters PDB tracing.

    The module pdb defines an interactive source code debugger for Python programs. It supports setting (conditional) breakpoints and single stepping at the source line level, inspection of stack frames, source code listing, and evaluation of arbitrary Python code in the context of any stack frame. It also supports post-mortem debugging and can be called under program control.

Related articles