Using Test Tags in Python unittest and pytest for Efficient Test Management
This article explains how to apply test tags in Python's unittest and pytest frameworks, providing multiple code examples that demonstrate custom attributes, class-level tags, markers, multi‑tags, parameterized tests, dynamic tagging, custom plugins, and report customization to improve test selection and organization in large automation suites.
In large-scale automated testing frameworks, the number of test cases can be massive. Test tags (or markers) help quickly locate and run specific categories of tests such as regression, smoke, or performance tests. In Python, this can be achieved using custom attributes in the unittest framework or markers in pytest.
Example 1: Using unittest custom attributes
import unittest
class TestAPI(unittest.TestCase):
@property
def test_tags(self):
return ['unit']
def test_login(self):
self.assertTrue(True)
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(TestAPI)
for test in suite:
print(f'Test: {test.id()}, Tags: {getattr(test, "test_tags", [])}')Example 2: Using unittest setUpClass method
import unittest
class TestAPI(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.test_tags = ['functional']
def test_api_functionality(self):
self.assertTrue(True)
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(TestAPI)
for test in suite:
print(f'Test: {test.id()}, Tags: {getattr(test.__class__, "test_tags", [])}')Example 3: Using pytest markers
import pytest
@pytest.mark.smoke
def test_smoke():
assert True
if __name__ == '__main__':
pytest.main(['-m', 'smoke'])Example 4: Multiple markers
import pytest
@pytest.mark.regression
@pytest.mark.smoke
def test_regress_and_smoke():
assert True
if __name__ == '__main__':
pytest.main(['-m', 'smoke or regression'])Example 5: Marking an entire test class
import pytest
@pytest.mark.performance
class TestPerformance:
def test_performance(self):
assert True
if __name__ == '__main__':
pytest.main(['-m', 'performance'])Example 6: Using marker filters
import pytest
@pytest.mark.long_running
def test_long_running():
assert True
if __name__ == '__main__':
pytest.main(['-m', 'not long_running'])Example 7: Parameterized tests with markers
import pytest
@pytest.mark.parametrize("input, expected", [(1, 1), (2, 2)])
@pytest.mark.functional
def test_parametrized(input, expected):
assert input == expected
if __name__ == '__main__':
pytest.main(['-m', 'functional'])Example 8: Dynamic tagging
import pytest
def pytest_collection_modifyitems(items):
for item in items:
if 'login' in item.name:
item.add_marker(pytest.mark.smoke)
if __name__ == '__main__':
pytest.main()Example 9: Custom marker plugin
# conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption('--tag', action='store', default='all', help='run tests with specified tag')
def pytest_collection_modifyitems(config, items):
selected_tag = config.getoption('tag')
if selected_tag != 'all':
skip_tag = pytest.mark.skip(reason=f'not running tests with tag {selected_tag}')
for item in items:
if selected_tag not in [mark.name for mark in item.iter_markers()]:
item.add_marker(skip_tag)
# test_example.py
import pytest
@pytest.mark.smoke
def test_smoke():
assert True
if __name__ == '__main__':
pytest.main(['--tag', 'smoke'])Example 10: Customizing test reports with markers
import pytest
def pytest_terminal_summary(terminalreporter):
passed_smoke_tests = terminalreporter.stats.get('passed', [])
smoke_tests = [item for item in passed_smoke_tests if 'smoke' in [mark.name for mark in item.keywords]]
print(f'\nSmoke Tests Passed: {len(smoke_tests)}')
if __name__ == '__main__':
pytest.main()These examples demonstrate how to use test tags in Python's unittest and pytest frameworks, enhancing test selectivity and organization, especially in large projects, thereby significantly improving testing efficiency and effectiveness.
If you are looking for a systematic way to manage your automated tests, consider incorporating test tags into your testing strategy; they help organize tests and provide flexibility in CI/CD pipelines.
For more Python programming and automated testing tips, follow our WeChat subscription account.
Test Development Learning Exchange
Test Development Learning Exchange
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.