pytest-tricks


Customize How py.test Collects Tests From Classes

Post

Date
Apr 20, 2016
Author
Bruno Oliveira
Tags
collection, hook

TL;DR

Use pytest_pycollect_makeitem to customize how tests are collected from classes

Star Fork

By default py.test collects all classes which start with Test that are found in test*.py or *test.py modules. This behavior can be configured using the python_classes and python_files configuration options.

Also, it will out-of-the box collect classes which subclass unittest.TestCase (regardless of their name), making it easy to run existing test suites.

But thanks to py.test's flexible plugin architecture, one can customize how tests are collected from classes to suit almost any requirement.

Collect All Subclasses of a Certain Class

Recently someone asked in the pytest-dev mailing list how to make py.test collect all classes, that subclass from some specific base class, independently of their name.

Suppose all your test classes subclass from a certain utility class:

class TestingUtils(object):

    def connect_to_database(self):
        ...

    def validate_ui(self):
        ...

And all your tests are written in functions in subclasses of TestingUtils, but that don't follow any particular naming convention:

class BorgTests(TestingUtils):

    def test_borg_creation(self):
        db = self.connect_to_database()
        ...

class ValidationRules(TestingUtils):

    def test_rule_1(self):
        ...

You can implement your own collection rules by implementing the pytest_pycollect_makeitem hook.

Simply add this code to a conftest.py file at the root of your tests' directory and that's it:

import inspect

def pytest_pycollect_makeitem(collector, name, obj):
    if inspect.isclass(obj) and issubclass(obj, TestingUtils):
        Class = collector._getcustomclass("Class")
        return Class(name, parent=collector)

This won't interfere with the normal test collection mechanism, only add to it, so classes prefixed with Test will also be collected as usual.

COMMENTS