Master Python Testing: Essential unittest & pytest Decorators Explained
This article introduces the most frequently used decorators in Python's unittest and pytest frameworks, demonstrates generic class and custom decorators, and shows how to apply Flask testing decorators for web API testing, all with clear code examples.
unittest framework common decorators
The unittest module provides several decorators to control test execution:
@unittest.skip("reason") – skips a specific test case.
@unittest.skipIf(condition, "reason") – skips when a condition is true.
@unittest.skipUnless(condition, "reason") – runs only when a condition is true.
@unittest.expectedFailure – marks a test that is expected to fail; a passing result is reported as a failure.
import unittest
class TestExample(unittest.TestCase):
@unittest.skip("temporarily skip this test")
def test_skip_example(self):
self.assertEqual(1, 1)
import sys, unittest
class TestSkipIf(unittest.TestCase):
@unittest.skipIf(sys.version_info < (3, 8), "requires Python 3.8+")
def test_skip_if(self):
self.assertTrue(True)
class TestUnixOnly(unittest.TestCase):
@unittest.skipUnless(os.name == 'posix', "Unix only")
def test_unix_only(self):
pass
class TestExpectedFailure(unittest.TestCase):
@unittest.expectedFailure
def test_expected_failure(self):
self.assertEqual(1, 2) # expected failurepytest framework common decorators
Pytest offers analogous decorators for skipping, conditional skipping, expected failures, and parameterized testing:
@pytest.mark.skip(reason="...") – skips the test.
@pytest.mark.skipif(condition, reason="...") – skips when the condition is true.
@pytest.mark.xfail – marks a test as expected to fail.
@pytest.mark.parametrize – runs a test multiple times with different arguments.
import pytest
@pytest.mark.skip(reason="feature not finished")
def test_skip():
assert False
import sys, pytest
@pytest.mark.skipif(sys.version_info < (3, 9), reason="requires Python 3.9")
def test_skipif():
assert True
def test_xfail():
assert False # expected failure
@pytest.mark.parametrize("a, b, expected", [
(1, 1, 2),
(2, 3, 5),
(-1, 1, 0),
])
def test_add(a, b, expected):
assert a + b == expectedGeneral decorators (applicable to any framework)
Beyond testing‑specific decorators, Python’s built‑in @classmethod and @staticmethod define class‑level and static methods, while custom decorators can add logging, timing, or retry logic.
class TestUtil:
@classmethod
def setUpClass(cls):
print("Setup before all tests")
@staticmethod
def helper_method():
return "helper"
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.time() - start:.4f}s")
return result
return wrapper
@timer
def test_performance():
time.sleep(1)
import time
def retry(max_attempts=3, delay=1):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if i < max_attempts - 1:
print(f"Error: {e}, retrying...")
time.sleep(delay)
else:
raise
return None
return wrapper
return decorator
@retry(max_attempts=3)
def fetch_data():
# simulate failure
import random
if random.random() < 0.7:
raise ConnectionError("Network error")
return "Success"
print(fetch_data())Flask testing decorators (Web API testing)
When testing Flask applications, the flask_testing.TestCase class and Flask’s route decorator are commonly used.
from flask import Flask
from flask_testing import TestCase
app = Flask(__name__)
@app.route("/hello")
def hello():
return "Hello World!"
class MyTest(TestCase):
def create_app(self):
app.config['TESTING'] = True
return app
def test_hello(self):
response = self.client.get('/hello')
self.assertEqual(response.data, b'Hello World!')Summary of decorator usage
Below is a visual classification of common decorator purposes.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
