Skipping Tests

Pytest filtering options are used to run only the desired tests, but it may be useful to skip a test based on some external condition: for example if the product was built with a certain feature or not. If the feature is not supported, tests that are using this feature must be skipped.

It is not possible to accomplish this with the built-in pytest.mark.skipif marker since it is evaluated too soon and only takes expressions, it does not provide access to the fixtures. However, pytest-mh provides alternative solutions.

See also

Every project has its own specifics and it is not possible to implement generic feature detection and provide it out of the box. ldap.features property used in these examples is not part of pytest-mh. To see tips on how to implement feature detection for your project, see Features Detection.

Skipping individual tests

It is possible to skip individual tests with pytest.mark.require marker. This marker takes a callable as parameter, if the callable evaluates to True the test is run. If it evaluates to False, the test is skipped.

Parameters of the callable are all fixtures that are available to the test, including all MultihostRole objects required by the test, that is all dynamic fixtures defined by the topology.

Warning

The skip condition is evaluated before test setup is performed to avoid complex setup and teardown for a test that is going to be skipped.

Keep this in mind when accessing the role objects, since the role and its utilities setup method has not yet been called, therefore some properties may not be correctly initialized. It is up to you to make sure that you only access the properties that have been already set in your code.

However, it is perfectly fine to run commands and access properties that do not depend on the setup.

Examples of pytest.mark.require
 1# Use a simple lambda function
 2@pytest.mark.topology(KnownTopology.LDAP)
 3@pytest.mark.require(
 4    lambda ldap: "password_policy" in ldap.features,
 5    "Server is not built with password policy support"
 6)
 7def test_skip__lambda(client: Client, ldap: LDAP):
 8    pass
 9
10
11# Use a defined function
12def require_password_policy(ldap: LDAP):
13    return "password_policy" in ldap.features
14
15@pytest.mark.topology(KnownTopology.LDAP)
16@pytest.mark.require(
17    require_password_policy,
18    "Server is not built with password policy support"
19)
20def test_skip__function(client: Client, ldap: LDAP):
21    pass
22
23
24# Use a defined function that also returns a reason in a tuple
25def require_password_policy(ldap: LDAP):
26    result = "password_policy" in ldap.features
27    reason = "Server is not built with password policy support"
28
29    return result, reason
30
31@pytest.mark.topology(KnownTopology.LDAP)
32@pytest.mark.require.with_args(require_password_policy)
33def test_skip__function_and_reason(client: Client, ldap: LDAP):
34    pass

Note

Notice the usage of with_args in the third example test_skip__function_and_reason. Pytest marker does not allow single function as an argument and it must be worked around by using with_args.

See pytest documentation for more information: pytest.MarkDecorator.with_args()

tests/test_passkey.py::test_skip__lambda (ldap) SKIPPED (Server is not built with password policy support)
tests/test_passkey.py::test_skip__function (ldap) SKIPPED (Server is not built with password policy support)
tests/test_passkey.py::test_skip__function_and_reason (ldap) SKIPPED (Server is not built with password policy support)

Skipping topology

Sometimes, it is not possible to run any tests from specific topology even though all hosts and roles required by the topology are available – for example when your program was not built with functionality required to correctly setup the topology. It is possible to achieve this by setting a skip condition by overriding the skip() method of TopologyController.

All dynamic fixtures defined by the topology are passed to the method, but this time they are instances of MultihostHost instead of MultihostRole since the role objects are only created for tests and are not available at this point.

Warning

The skip condition is evaluated before topology setup is performed to avoid complex setup and teardown for tests that are going to be skipped.

Keep this in mind when accessing the host objects, since the hosts and its utilities setup method has not yet been called, therefore some properties may not be correctly initialized. It is up to you to make sure that you only access the properties that have been already set in your code.

However, it is perfectly fine to run commands and access properties that do not depend on the setup.

Examples of TopologyController.skip()
class PasswordPolicyTopology(TopologyController):
    def skip(self, ldap: LDAPHost) -> str | None:
        if "password_policy" not in ldap.features:
            # Return reason to skip the tests
            return "Server is not built with password policy support"

        # Return None to run the tests
        return None
tests/test_passkey.py::test_skip__lambda (ldap) SKIPPED (Server is not built with password policy support)
tests/test_passkey.py::test_skip__function (ldap) SKIPPED (Server is not built with password policy support)
tests/test_passkey.py::test_skip__function_and_reason (ldap) SKIPPED (Server is not built with password policy support)