$npx -y skills add LambdaTest/agent-skills --skill pytest-skillGenerates production-grade pytest tests in Python with fixtures, parametrize, markers, mocking, and conftest patterns. Use when user mentions "pytest", "conftest", "@pytest.fixture", "@pytest.mark", "Python test". Triggers on: "pytest", "conftest", "Python test", "parametrize", "
| 1 | # Pytest Testing Skill |
| 2 | |
| 3 | ## Core Patterns |
| 4 | |
| 5 | ### Basic Test |
| 6 | |
| 7 | ```python |
| 8 | import pytest |
| 9 | |
| 10 | def test_addition(): |
| 11 | assert 2 + 3 == 5 |
| 12 | |
| 13 | def test_exception(): |
| 14 | with pytest.raises(ValueError, match="invalid"): |
| 15 | int("not_a_number") |
| 16 | |
| 17 | class TestCalculator: |
| 18 | def test_add(self): |
| 19 | calc = Calculator() |
| 20 | assert calc.add(2, 3) == 5 |
| 21 | |
| 22 | def test_divide_by_zero(self): |
| 23 | with pytest.raises(ZeroDivisionError): |
| 24 | Calculator().divide(10, 0) |
| 25 | ``` |
| 26 | |
| 27 | ### Fixtures |
| 28 | |
| 29 | ```python |
| 30 | @pytest.fixture |
| 31 | def calculator(): |
| 32 | return Calculator() |
| 33 | |
| 34 | @pytest.fixture |
| 35 | def db_connection(): |
| 36 | conn = Database.connect("test_db") |
| 37 | yield conn # teardown after yield |
| 38 | conn.rollback() |
| 39 | conn.close() |
| 40 | |
| 41 | @pytest.fixture(scope="module") |
| 42 | def api_client(): |
| 43 | client = APIClient(base_url="http://localhost:8000") |
| 44 | yield client |
| 45 | client.logout() |
| 46 | |
| 47 | # conftest.py - shared fixtures |
| 48 | @pytest.fixture(autouse=True) |
| 49 | def reset_state(): |
| 50 | State.reset() |
| 51 | yield |
| 52 | State.cleanup() |
| 53 | |
| 54 | # Usage |
| 55 | def test_add(calculator): |
| 56 | assert calculator.add(2, 3) == 5 |
| 57 | ``` |
| 58 | |
| 59 | ### Parametrize |
| 60 | |
| 61 | ```python |
| 62 | @pytest.mark.parametrize("input,expected", [ |
| 63 | ("hello", 5), ("", 0), ("pytest", 6), |
| 64 | ]) |
| 65 | def test_string_length(input, expected): |
| 66 | assert len(input) == expected |
| 67 | |
| 68 | @pytest.mark.parametrize("a,b,expected", [ |
| 69 | (2, 3, 5), (-1, 1, 0), (0, 0, 0), |
| 70 | ]) |
| 71 | def test_add(calculator, a, b, expected): |
| 72 | assert calculator.add(a, b) == expected |
| 73 | ``` |
| 74 | |
| 75 | ### Markers |
| 76 | |
| 77 | ```python |
| 78 | @pytest.mark.slow |
| 79 | def test_large_dataset(): ... |
| 80 | |
| 81 | @pytest.mark.skip(reason="Not implemented") |
| 82 | def test_future_feature(): ... |
| 83 | |
| 84 | @pytest.mark.skipif(sys.platform == "win32", reason="Unix only") |
| 85 | def test_unix_permissions(): ... |
| 86 | |
| 87 | @pytest.mark.xfail(reason="Known bug #123") |
| 88 | def test_known_bug(): ... |
| 89 | ``` |
| 90 | |
| 91 | ### Mocking |
| 92 | |
| 93 | ```python |
| 94 | from unittest.mock import patch, MagicMock |
| 95 | |
| 96 | def test_send_email(mocker): |
| 97 | mock_smtp = mocker.patch("myapp.email.smtplib.SMTP") |
| 98 | send_welcome_email("user@test.com") |
| 99 | mock_smtp.return_value.sendmail.assert_called_once() |
| 100 | |
| 101 | def test_api_call(mocker): |
| 102 | mock_response = mocker.Mock() |
| 103 | mock_response.status_code = 200 |
| 104 | mock_response.json.return_value = {"users": [{"name": "Alice"}]} |
| 105 | mocker.patch("myapp.service.requests.get", return_value=mock_response) |
| 106 | users = get_users() |
| 107 | assert len(users) == 1 |
| 108 | |
| 109 | @patch("myapp.service.database") |
| 110 | def test_save_user(mock_db): |
| 111 | mock_db.save.return_value = True |
| 112 | assert save_user({"name": "Alice"}) is True |
| 113 | mock_db.save.assert_called_once() |
| 114 | ``` |
| 115 | |
| 116 | ### Assertions |
| 117 | |
| 118 | ```python |
| 119 | assert x == y |
| 120 | assert x != y |
| 121 | assert x in collection |
| 122 | assert isinstance(obj, MyClass) |
| 123 | assert 0.1 + 0.2 == pytest.approx(0.3) |
| 124 | |
| 125 | with pytest.raises(ValueError) as exc_info: |
| 126 | raise ValueError("bad") |
| 127 | assert "bad" in str(exc_info.value) |
| 128 | ``` |
| 129 | |
| 130 | ### Anti-Patterns |
| 131 | |
| 132 | | Bad | Good | Why | |
| 133 | |-----|------|-----| |
| 134 | | `self.assertEqual()` | `assert x == y` | pytest rewrites give better output | |
| 135 | | Setup in `__init__` | `@pytest.fixture` | Lifecycle management | |
| 136 | | Global state | Fixture with `yield` | Proper cleanup | |
| 137 | | Huge test functions | Small focused tests | Easier debugging | |
| 138 | |
| 139 | ## Quick Reference |
| 140 | |
| 141 | | Task | Command | |
| 142 | |------|---------| |
| 143 | | Run all | `pytest` | |
| 144 | | Run file | `pytest tests/test_login.py` | |
| 145 | | Run specific | `pytest tests/test_login.py::test_login_success` | |
| 146 | | By marker | `pytest -m slow` | |
| 147 | | By keyword | `pytest -k "login and not invalid"` | |
| 148 | | Verbose | `pytest -v` | |
| 149 | | Stop first fail | `pytest -x` | |
| 150 | | Last failed | `pytest --lf` | |
| 151 | | Coverage | `pytest --cov=myapp --cov-report=html` | |
| 152 | | Parallel | `pytest -n auto` (pytest-xdist) | |
| 153 | |
| 154 | ## pyproject.toml |
| 155 | |
| 156 | ```toml |
| 157 | [tool.pytest.ini_options] |
| 158 | testpaths = ["tests"] |
| 159 | markers = ["slow: slow tests", "integration: integration tests"] |
| 160 | addopts = "-v --tb=short" |
| 161 | ``` |
| 162 | |
| 163 | ## Deep Patterns |
| 164 | |
| 165 | For production-grade patterns, see `reference/playbook.md`: |
| 166 | |
| 167 | | Section | What's Inside | |
| 168 | |---------|--------------| |
| 169 | | §1 Config | pytest.ini + pyproject.toml with markers, coverage | |
| 170 | | §2 Fixtures | Scoping, factories, teardown, autouse, tmp_path | |
| 171 | | §3 Parametrize | Basic, with IDs, cartesian, indirect | |
| 172 | | §4 Mocking | pytest-mock, monkeypatch, spies, env vars | |
| 173 | | §5 Async | pytest-asyncio, async fixtures, async client | |
| 174 | | §6 Exceptions | pytest.raises(match=), warnings | |
| 175 | | §7 Markers & Plugins | Custom markers, collection hooks | |
| 176 | | §8 Class-Based | Nested classes, autouse setup | |
| 177 | | §9 CI/CD | GitHub Actions matrix, coverage gates | |
| 178 | | §10 Debugging Table | 10 common problems with fixes | |
| 179 | | §11 Best Practices | 15-item production checklist | |