@sylix/coworker 2.0.11 → 2.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/commands/slash/config.d.ts.map +1 -1
  2. package/dist/commands/slash/config.js +22 -4
  3. package/dist/commands/slash/config.js.map +1 -1
  4. package/dist/core/CoWorkerAgent.d.ts.map +1 -1
  5. package/dist/core/CoWorkerAgent.js +6 -3
  6. package/dist/core/CoWorkerAgent.js.map +1 -1
  7. package/dist/skills/defaults/accessibility/screen-reader-testing.md +545 -0
  8. package/dist/skills/defaults/accessibility/wcag-audit-patterns.md +555 -0
  9. package/dist/skills/defaults/ai-ml/rag.md +276 -0
  10. package/dist/skills/defaults/backend-development/api-design-principles.md +528 -0
  11. package/dist/skills/defaults/backend-development/api-design.md +285 -0
  12. package/dist/skills/defaults/backend-development/architecture-patterns.md +494 -0
  13. package/dist/skills/defaults/backend-development/async-python.md +237 -0
  14. package/dist/skills/defaults/backend-development/auth-implementation-patterns.md +638 -0
  15. package/dist/skills/defaults/backend-development/bazel-build-optimization.md +387 -0
  16. package/dist/skills/defaults/backend-development/billing-automation/SKILL.md +566 -0
  17. package/dist/skills/defaults/backend-development/code-review-excellence.md +538 -0
  18. package/dist/skills/defaults/backend-development/cqrs-implementation.md +554 -0
  19. package/dist/skills/defaults/backend-development/database-design.md +305 -0
  20. package/dist/skills/defaults/backend-development/debugging-strategies.md +536 -0
  21. package/dist/skills/defaults/backend-development/e2e-testing-patterns.md +544 -0
  22. package/dist/skills/defaults/backend-development/error-handling-patterns.md +641 -0
  23. package/dist/skills/defaults/backend-development/fastapi-templates.md +559 -0
  24. package/dist/skills/defaults/backend-development/fastapi.md +309 -0
  25. package/dist/skills/defaults/backend-development/git-advanced-workflows.md +405 -0
  26. package/dist/skills/defaults/backend-development/microservices-patterns.md +595 -0
  27. package/dist/skills/defaults/backend-development/microservices.md +284 -0
  28. package/dist/skills/defaults/backend-development/monorepo-management.md +623 -0
  29. package/dist/skills/defaults/backend-development/nodejs-backend-patterns.md +1048 -0
  30. package/dist/skills/defaults/backend-development/nx-workspace-patterns.md +457 -0
  31. package/dist/skills/defaults/backend-development/paypal-integration/SKILL.md +478 -0
  32. package/dist/skills/defaults/backend-development/pci-compliance/SKILL.md +480 -0
  33. package/dist/skills/defaults/backend-development/python-anti-patterns.md +349 -0
  34. package/dist/skills/defaults/backend-development/python-background-jobs.md +364 -0
  35. package/dist/skills/defaults/backend-development/python-code-style.md +360 -0
  36. package/dist/skills/defaults/backend-development/python-configuration.md +368 -0
  37. package/dist/skills/defaults/backend-development/python-design-patterns.md +296 -0
  38. package/dist/skills/defaults/backend-development/python-error-handling.md +323 -0
  39. package/dist/skills/defaults/backend-development/python-packaging.md +887 -0
  40. package/dist/skills/defaults/backend-development/python-performance-optimization.md +874 -0
  41. package/dist/skills/defaults/backend-development/python-project-structure.md +252 -0
  42. package/dist/skills/defaults/backend-development/python-resilience.md +376 -0
  43. package/dist/skills/defaults/backend-development/python-resource-management.md +421 -0
  44. package/dist/skills/defaults/backend-development/python-type-safety.md +428 -0
  45. package/dist/skills/defaults/backend-development/sql-optimization-patterns.md +509 -0
  46. package/dist/skills/defaults/backend-development/stripe-integration/SKILL.md +522 -0
  47. package/dist/skills/defaults/backend-development/turborepo-caching.md +376 -0
  48. package/dist/skills/defaults/blockchain/defi-protocol-templates.md +430 -0
  49. package/dist/skills/defaults/blockchain/nft-standards.md +364 -0
  50. package/dist/skills/defaults/blockchain/solidity-security.md +514 -0
  51. package/dist/skills/defaults/blockchain/web3-testing.md +360 -0
  52. package/dist/skills/defaults/business/competitive-landscape/SKILL.md +527 -0
  53. package/dist/skills/defaults/business/market-sizing-analysis/SKILL.md +451 -0
  54. package/dist/skills/defaults/business/startup-financial-modeling/SKILL.md +494 -0
  55. package/dist/skills/defaults/business/startup-metrics-framework/SKILL.md +564 -0
  56. package/dist/skills/defaults/business/team-composition-analysis.md +437 -0
  57. package/dist/skills/defaults/compliance/employment-contract-templates/SKILL.md +527 -0
  58. package/dist/skills/defaults/compliance/gdpr-data-handling/SKILL.md +630 -0
  59. package/dist/skills/defaults/data-engineering/airflow-dag-patterns.md +436 -0
  60. package/dist/skills/defaults/data-engineering/airflow.md +519 -0
  61. package/dist/skills/defaults/data-engineering/data-quality.md +583 -0
  62. package/dist/skills/defaults/data-engineering/dbt-transformation-patterns.md +482 -0
  63. package/dist/skills/defaults/data-engineering/dbt.md +556 -0
  64. package/dist/skills/defaults/data-engineering/ml-pipeline-workflow/SKILL.md +247 -0
  65. package/dist/skills/defaults/data-engineering/spark-optimization.md +348 -0
  66. package/dist/skills/defaults/data-engineering/spark.md +411 -0
  67. package/dist/skills/defaults/database/postgresql.md +202 -0
  68. package/dist/skills/defaults/debugging/systematic-debugging.md +249 -0
  69. package/dist/skills/defaults/devops/architecture-decision-records.md +448 -0
  70. package/dist/skills/defaults/devops/changelog-automation.md +580 -0
  71. package/dist/skills/defaults/devops/cicd.md +314 -0
  72. package/dist/skills/defaults/devops/cloud.md +263 -0
  73. package/dist/skills/defaults/devops/code-review-excellence.md +299 -0
  74. package/dist/skills/defaults/devops/cost-optimization.md +295 -0
  75. package/dist/skills/defaults/devops/deployment-pipeline-design.md +356 -0
  76. package/dist/skills/defaults/devops/docker.md +281 -0
  77. package/dist/skills/defaults/devops/git-workflows.md +205 -0
  78. package/dist/skills/defaults/devops/github-actions.md +311 -0
  79. package/dist/skills/defaults/devops/gitlab-ci-patterns.md +266 -0
  80. package/dist/skills/defaults/devops/hybrid-cloud-networking.md +241 -0
  81. package/dist/skills/defaults/devops/istio-traffic-management.md +327 -0
  82. package/dist/skills/defaults/devops/kubernetes.md +339 -0
  83. package/dist/skills/defaults/devops/linkerd-patterns.md +311 -0
  84. package/dist/skills/defaults/devops/multi-cloud-architecture.md +181 -0
  85. package/dist/skills/defaults/devops/observability.md +243 -0
  86. package/dist/skills/defaults/devops/openapi-spec-generation.md +1024 -0
  87. package/dist/skills/defaults/devops/postmortem-writing.md +396 -0
  88. package/dist/skills/defaults/devops/prometheus-configuration.md +265 -0
  89. package/dist/skills/defaults/devops/secrets-management.md +341 -0
  90. package/dist/skills/defaults/devops/service-mesh-observability.md +385 -0
  91. package/dist/skills/defaults/devops/terraform-module-library.md +244 -0
  92. package/dist/skills/defaults/finance/backtesting-frameworks/SKILL.md +663 -0
  93. package/dist/skills/defaults/finance/risk-metrics-calculation/SKILL.md +557 -0
  94. package/dist/skills/defaults/frontend/accessibility-compliance.md +420 -0
  95. package/dist/skills/defaults/frontend/design-system-patterns.md +337 -0
  96. package/dist/skills/defaults/frontend/interaction-design.md +327 -0
  97. package/dist/skills/defaults/frontend/javascript.md +311 -0
  98. package/dist/skills/defaults/frontend/modern-javascript-patterns.md +927 -0
  99. package/dist/skills/defaults/frontend/react-native-design.md +440 -0
  100. package/dist/skills/defaults/frontend/react.md +345 -0
  101. package/dist/skills/defaults/frontend/responsive-design.md +472 -0
  102. package/dist/skills/defaults/frontend/tailwind-design-system.md +337 -0
  103. package/dist/skills/defaults/frontend/typescript-advanced-types.md +724 -0
  104. package/dist/skills/defaults/frontend/typescript.md +334 -0
  105. package/dist/skills/defaults/frontend/visual-design-foundations.md +326 -0
  106. package/dist/skills/defaults/frontend/web-component-design.md +279 -0
  107. package/dist/skills/defaults/game-development/godot-gdscript-patterns.md +188 -0
  108. package/dist/skills/defaults/game-development/unity-ecs-patterns.md +594 -0
  109. package/dist/skills/defaults/kubernetes/gitops-workflow.md +285 -0
  110. package/dist/skills/defaults/kubernetes/gitops.md +280 -0
  111. package/dist/skills/defaults/kubernetes/helm-chart-scaffolding.md +553 -0
  112. package/dist/skills/defaults/kubernetes/helm.md +343 -0
  113. package/dist/skills/defaults/kubernetes/k8s-manifest-generator.md +501 -0
  114. package/dist/skills/defaults/kubernetes/k8s-security-policies.md +342 -0
  115. package/dist/skills/defaults/kubernetes/manifests.md +330 -0
  116. package/dist/skills/defaults/kubernetes/security.md +337 -0
  117. package/dist/skills/defaults/llm-application/embedding-strategies.md +608 -0
  118. package/dist/skills/defaults/llm-application/hybrid-search-implementation.md +570 -0
  119. package/dist/skills/defaults/llm-application/hybrid-search.md +570 -0
  120. package/dist/skills/defaults/llm-application/langchain-architecture.md +666 -0
  121. package/dist/skills/defaults/llm-application/langchain.md +259 -0
  122. package/dist/skills/defaults/llm-application/llm-evaluation.md +695 -0
  123. package/dist/skills/defaults/llm-application/prompt-engineering-patterns.md +449 -0
  124. package/dist/skills/defaults/llm-application/prompt-engineering.md +219 -0
  125. package/dist/skills/defaults/llm-application/rag-implementation.md +434 -0
  126. package/dist/skills/defaults/llm-application/similarity-search-patterns.md +560 -0
  127. package/dist/skills/defaults/llm-application/similarity-search.md +560 -0
  128. package/dist/skills/defaults/llm-application/vector-index-tuning.md +523 -0
  129. package/dist/skills/defaults/mobile/mobile-android-design.md +440 -0
  130. package/dist/skills/defaults/mobile/mobile-ios-design.md +266 -0
  131. package/dist/skills/defaults/monitoring/distributed-tracing.md +436 -0
  132. package/dist/skills/defaults/monitoring/grafana-dashboards.md +370 -0
  133. package/dist/skills/defaults/monitoring/prometheus-configuration.md +379 -0
  134. package/dist/skills/defaults/monitoring/slo-implementation.md +323 -0
  135. package/dist/skills/defaults/refactoring/code-refactoring.md +349 -0
  136. package/dist/skills/defaults/security/anti-reversing-techniques/SKILL.md +559 -0
  137. package/dist/skills/defaults/security/auditor.md +168 -0
  138. package/dist/skills/defaults/security/binary-analysis-patterns/SKILL.md +438 -0
  139. package/dist/skills/defaults/security/memory-forensics/SKILL.md +483 -0
  140. package/dist/skills/defaults/security/mtls-configuration.md +349 -0
  141. package/dist/skills/defaults/security/protocol-reverse-engineering/SKILL.md +520 -0
  142. package/dist/skills/defaults/security/sast-configuration.md +182 -0
  143. package/dist/skills/defaults/security/security.md +313 -0
  144. package/dist/skills/defaults/security/stride-analysis.md +273 -0
  145. package/dist/skills/defaults/security/threat-mitigation-mapping.md +290 -0
  146. package/dist/skills/defaults/systems/bash-defensive-patterns/SKILL.md +539 -0
  147. package/dist/skills/defaults/systems/bats-testing-patterns/SKILL.md +631 -0
  148. package/dist/skills/defaults/systems/go-concurrency-patterns.md +657 -0
  149. package/dist/skills/defaults/systems/memory-safety-patterns.md +605 -0
  150. package/dist/skills/defaults/systems/rust-async-patterns.md +519 -0
  151. package/dist/skills/defaults/systems/shellcheck-configuration/SKILL.md +456 -0
  152. package/dist/skills/defaults/team-collaboration/multi-reviewer-patterns.md +126 -0
  153. package/dist/skills/defaults/team-collaboration/parallel-feature-development.md +151 -0
  154. package/dist/skills/defaults/testing/javascript-testing-patterns.md +1021 -0
  155. package/dist/skills/defaults/testing/python-testing-patterns.md +351 -0
  156. package/dist/skills/defaults/testing/testing.md +332 -0
  157. package/dist/skills/defaults/workflows/context-driven-development.md +384 -0
  158. package/dist/skills/defaults/workflows/track-management.md +592 -0
  159. package/dist/skills/defaults/workflows/workflow-patterns.md +622 -0
  160. package/dist/skills/index.d.ts +11 -0
  161. package/dist/skills/index.d.ts.map +1 -0
  162. package/dist/skills/index.js +129 -0
  163. package/dist/skills/index.js.map +1 -0
  164. package/dist/utils/character.js +4 -4
  165. package/dist/utils/character.js.map +1 -1
  166. package/dist/utils/inputbar.d.ts.map +1 -1
  167. package/dist/utils/inputbar.js +7 -0
  168. package/dist/utils/inputbar.js.map +1 -1
  169. package/package.json +1 -1
@@ -0,0 +1,351 @@
1
+ ---
2
+ name: python-testing-patterns
3
+ description: Implement comprehensive testing strategies with pytest, fixtures, mocking, and test-driven development. Use when writing Python tests, setting up test suites, or implementing testing best practices.
4
+ ---
5
+
6
+ # Python Testing Patterns
7
+
8
+ Comprehensive guide to implementing robust testing strategies in Python using pytest, fixtures, mocking, parameterization, and test-driven development practices.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Writing unit tests for Python code
13
+ - Setting up test suites and test infrastructure
14
+ - Implementing test-driven development (TDD)
15
+ - Creating integration tests for APIs and services
16
+ - Mocking external dependencies and services
17
+ - Testing async code and concurrent operations
18
+ - Setting up continuous testing in CI/CD
19
+ - Implementing property-based testing
20
+ - Testing database operations
21
+ - Debugging failing tests
22
+
23
+ ## Core Concepts
24
+
25
+ ### 1. Test Types
26
+
27
+ - **Unit Tests**: Test individual functions/classes in isolation
28
+ - **Integration Tests**: Test interaction between components
29
+ - **Functional Tests**: Test complete features end-to-end
30
+ - **Performance Tests**: Measure speed and resource usage
31
+
32
+ ### 2. Test Structure (AAA Pattern)
33
+
34
+ - **Arrange**: Set up test data and preconditions
35
+ - **Act**: Execute the code under test
36
+ - **Assert**: Verify the results
37
+
38
+ ### 3. Test Coverage
39
+
40
+ - Measure what code is exercised by tests
41
+ - Identify untested code paths
42
+ - Aim for meaningful coverage, not just high percentages
43
+
44
+ ## Quick Start
45
+
46
+ ```python
47
+ def add(a, b):
48
+ return a + b
49
+
50
+ def test_add():
51
+ result = add(2, 3)
52
+ assert result == 5
53
+ ```
54
+
55
+ ## Fundamental Patterns
56
+
57
+ ### Pattern 1: Basic pytest Tests
58
+
59
+ ```python
60
+ import pytest
61
+
62
+ class Calculator:
63
+ def add(self, a: float, b: float) -> float:
64
+ return a + b
65
+
66
+ def divide(self, a: float, b: float) -> float:
67
+ if b == 0:
68
+ raise ValueError("Cannot divide by zero")
69
+ return a / b
70
+
71
+ def test_addition():
72
+ calc = Calculator()
73
+ assert calc.add(2, 3) == 5
74
+
75
+ def test_division_by_zero():
76
+ calc = Calculator()
77
+ with pytest.raises(ValueError, match="Cannot divide by zero"):
78
+ calc.divide(5, 0)
79
+ ```
80
+
81
+ ### Pattern 2: Fixtures for Setup and Teardown
82
+
83
+ ```python
84
+ import pytest
85
+ from typing import Generator
86
+
87
+ class Database:
88
+ def __init__(self, connection_string: str):
89
+ self.connection_string = connection_string
90
+ self.connected = False
91
+
92
+ def connect(self):
93
+ self.connected = True
94
+
95
+ def disconnect(self):
96
+ self.connected = False
97
+
98
+ @pytest.fixture
99
+ def db() -> Generator[Database, None, None]:
100
+ database = Database("sqlite:///:memory:")
101
+ database.connect()
102
+ yield database
103
+ database.disconnect()
104
+
105
+ def test_database_query(db):
106
+ assert db.connected is True
107
+ ```
108
+
109
+ ### Pattern 3: Parameterized Tests
110
+
111
+ ```python
112
+ import pytest
113
+
114
+ def is_valid_email(email: str) -> bool:
115
+ return "@" in email and "." in email.split("@")[1]
116
+
117
+ @pytest.mark.parametrize("email,expected", [
118
+ ("user@example.com", True),
119
+ ("invalid.email", False),
120
+ ("@example.com", False),
121
+ ])
122
+ def test_email_validation(email, expected):
123
+ assert is_valid_email(email) == expected
124
+ ```
125
+
126
+ ### Pattern 4: Mocking with unittest.mock
127
+
128
+ ```python
129
+ import pytest
130
+ from unittest.mock import Mock, patch
131
+
132
+ class APIClient:
133
+ def __init__(self, base_url: str):
134
+ self.base_url = base_url
135
+
136
+ def get_user(self, user_id: int) -> dict:
137
+ import requests
138
+ response = requests.get(f"{self.base_url}/users/{user_id}")
139
+ response.raise_for_status()
140
+ return response.json()
141
+
142
+ def test_get_user_success():
143
+ client = APIClient("https://api.example.com")
144
+
145
+ mock_response = Mock()
146
+ mock_response.json.return_value = {"id": 1, "name": "John Doe"}
147
+ mock_response.raise_for_status.return_value = None
148
+
149
+ with patch("requests.get", return_value=mock_response) as mock_get:
150
+ user = client.get_user(1)
151
+ assert user["id"] == 1
152
+ mock_get.assert_called_once_with("https://api.example.com/users/1")
153
+ ```
154
+
155
+ ### Pattern 5: Testing Exceptions
156
+
157
+ ```python
158
+ import pytest
159
+
160
+ def divide(a: float, b: float) -> float:
161
+ if b == 0:
162
+ raise ZeroDivisionError("Division by zero")
163
+ return a / b
164
+
165
+ def test_zero_division():
166
+ with pytest.raises(ZeroDivisionError):
167
+ divide(10, 0)
168
+
169
+ def test_exception_message():
170
+ with pytest.raises(ZeroDivisionError, match="Division by zero"):
171
+ divide(5, 0)
172
+ ```
173
+
174
+ ## Advanced Patterns
175
+
176
+ ### Pattern 6: Testing Async Code
177
+
178
+ ```python
179
+ import pytest
180
+ import asyncio
181
+
182
+ async def fetch_data(url: str) -> dict:
183
+ await asyncio.sleep(0.1)
184
+ return {"url": url, "data": "result"}
185
+
186
+ @pytest.mark.asyncio
187
+ async def test_fetch_data():
188
+ result = await fetch_data("https://api.example.com")
189
+ assert result["url"] == "https://api.example.com"
190
+
191
+ @pytest.mark.asyncio
192
+ async def test_concurrent_fetches():
193
+ urls = ["url1", "url2", "url3"]
194
+ tasks = [fetch_data(url) for url in urls]
195
+ results = await asyncio.gather(*tasks)
196
+ assert len(results) == 3
197
+ ```
198
+
199
+ ### Pattern 7: Monkeypatch for Testing
200
+
201
+ ```python
202
+ import os
203
+ import pytest
204
+
205
+ def get_database_url() -> str:
206
+ return os.environ.get("DATABASE_URL", "sqlite:///:memory:")
207
+
208
+ def test_database_url_custom(monkeypatch):
209
+ monkeypatch.setenv("DATABASE_URL", "postgresql://localhost/test")
210
+ assert get_database_url() == "postgresql://localhost/test"
211
+ ```
212
+
213
+ ### Pattern 8: Temporary Files and Directories
214
+
215
+ ```python
216
+ import pytest
217
+ from pathlib import Path
218
+
219
+ def save_data(filepath: Path, data: str):
220
+ filepath.write_text(data)
221
+
222
+ def test_file_operations(tmp_path):
223
+ test_file = tmp_path / "test_data.txt"
224
+ save_data(test_file, "Hello, World!")
225
+ assert test_file.exists()
226
+ assert test_file.read_text() == "Hello, World!"
227
+ ```
228
+
229
+ ### Pattern 9: Property-Based Testing
230
+
231
+ ```python
232
+ from hypothesis import given, strategies as st
233
+
234
+ def reverse_string(s: str) -> str:
235
+ return s[::-1]
236
+
237
+ @given(st.text())
238
+ def test_reverse_twice_is_original(s):
239
+ assert reverse_string(reverse_string(s)) == s
240
+
241
+ @given(st.integers(), st.integers())
242
+ def test_addition_commutative(a, b):
243
+ assert a + b == b + a
244
+ ```
245
+
246
+ ### Pattern 10: Testing Retry Behavior
247
+
248
+ ```python
249
+ from unittest.mock import Mock
250
+
251
+ def test_retries_on_transient_error():
252
+ client = Mock()
253
+ client.request.side_effect = [
254
+ ConnectionError("Failed"),
255
+ ConnectionError("Failed"),
256
+ {"status": "ok"},
257
+ ]
258
+
259
+ service = ServiceWithRetry(client, max_retries=3)
260
+ result = service.fetch()
261
+
262
+ assert result == {"status": "ok"}
263
+ assert client.request.call_count == 3
264
+ ```
265
+
266
+ ### Pattern 11: Mocking Time with Freezegun
267
+
268
+ ```python
269
+ from freezegun import freeze_time
270
+ from datetime import datetime
271
+
272
+ @freeze_time("2026-01-15 10:00:00")
273
+ def test_token_expiry():
274
+ token = create_token(expires_in_seconds=3600)
275
+ assert token.expires_at == datetime(2026, 1, 15, 11, 0, 0)
276
+ ```
277
+
278
+ ## Test Design Principles
279
+
280
+ ### One Behavior Per Test
281
+
282
+ ```python
283
+ # BAD - testing multiple behaviors
284
+ def test_user_service():
285
+ user = service.create_user(data)
286
+ assert user.id is not None
287
+ assert user.email == data["email"]
288
+
289
+ # GOOD - focused tests
290
+ def test_create_user_assigns_id():
291
+ user = service.create_user(data)
292
+ assert user.id is not None
293
+
294
+ def test_create_user_stores_email():
295
+ user = service.create_user(data)
296
+ assert user.email == data["email"]
297
+ ```
298
+
299
+ ## Testing Best Practices
300
+
301
+ 1. **Write tests first** (TDD) or alongside code
302
+ 2. **One assertion per test** when possible
303
+ 3. **Use descriptive test names** that explain behavior
304
+ 4. **Keep tests independent** and isolated
305
+ 5. **Use fixtures** for setup and teardown
306
+ 6. **Mock external dependencies** appropriately
307
+ 7. **Parametrize tests** to reduce duplication
308
+ 8. **Test edge cases** and error conditions
309
+ 9. **Measure coverage** but focus on quality
310
+ 10. **Run tests in CI/CD** on every commit
311
+
312
+ ## Test Organization
313
+
314
+ ```
315
+ tests/
316
+ conftest.py # Shared fixtures
317
+ test_unit/ # Unit tests
318
+ test_integration/ # Integration tests
319
+ test_e2e/ # End-to-end tests
320
+ ```
321
+
322
+ ## CI/CD Integration
323
+
324
+ ```yaml
325
+ name: Tests
326
+
327
+ on: [push, pull_request]
328
+
329
+ jobs:
330
+ test:
331
+ runs-on: ubuntu-latest
332
+ steps:
333
+ - uses: actions/checkout@v3
334
+ - name: Run tests
335
+ run: pytest --cov=myapp --cov-report=xml
336
+ - name: Upload coverage
337
+ uses: codecov/codecov-action@v3
338
+ ```
339
+
340
+ ## Configuration
341
+
342
+ ```toml
343
+ # pyproject.toml
344
+ [tool.pytest.ini_options]
345
+ testpaths = ["tests"]
346
+ addopts = ["-v", "--cov=myapp", "--cov-report=term-missing"]
347
+
348
+ [tool.coverage.run]
349
+ source = ["myapp"]
350
+ omit = ["*/tests/*", "*/migrations/*"]
351
+ ```
@@ -0,0 +1,332 @@
1
+ ---
2
+ name: testing
3
+ description: Write effective unit, integration, and E2E tests with modern testing patterns and best practices.
4
+ ---
5
+
6
+ # Testing Best Practices — CoWorker Edition
7
+
8
+ Write tests that give you confidence.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Writing new tests
13
+ - Improving test coverage
14
+ - Setting up test infrastructure
15
+ - Debugging test failures
16
+
17
+ ## Core Concepts
18
+
19
+ ### 1. Unit Tests
20
+
21
+ ```typescript
22
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
23
+ import { UserService } from './UserService';
24
+ import { UserRepository } from './UserRepository';
25
+
26
+ describe('UserService', () => {
27
+ let userService: UserService;
28
+ let mockRepository: UserRepository;
29
+
30
+ beforeEach(() => {
31
+ // Create mock
32
+ mockRepository = {
33
+ findById: vi.fn(),
34
+ save: vi.fn(),
35
+ delete: vi.fn()
36
+ } as UserRepository;
37
+
38
+ userService = new UserService(mockRepository);
39
+ });
40
+
41
+ describe('getUser', () => {
42
+ it('should return user when found', async () => {
43
+ const mockUser = { id: '1', name: 'John' };
44
+ mockRepository.findById.mockResolvedValue(mockUser);
45
+
46
+ const result = await userService.getUser('1');
47
+
48
+ expect(result).toEqual(mockUser);
49
+ expect(mockRepository.findById).toHaveBeenCalledWith('1');
50
+ });
51
+
52
+ it('should throw when user not found', async () => {
53
+ mockRepository.findById.mockResolvedValue(null);
54
+
55
+ await expect(userService.getUser('999')).rejects.toThrow('User not found');
56
+ });
57
+ });
58
+
59
+ describe('createUser', () => {
60
+ it('should create user with hashed password', async () => {
61
+ mockRepository.save.mockResolvedValue({ id: '1', email: 'test@test.com' });
62
+
63
+ const result = await userService.createUser({
64
+ email: 'test@test.com',
65
+ password: 'plaintext123'
66
+ });
67
+
68
+ expect(mockRepository.save).toHaveBeenCalled();
69
+ const savedUser = mockRepository.save.mock.calls[0][0];
70
+ expect(savedUser.passwordHash).toBeDefined();
71
+ expect(savedUser.passwordHash).not.toBe('plaintext123');
72
+ });
73
+ });
74
+ });
75
+ ```
76
+
77
+ ### 2. Integration Tests
78
+
79
+ ```typescript
80
+ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
81
+ import { setupTestDatabase, teardownTestDatabase } from './testUtils';
82
+ import { app } from '../src/app';
83
+ import request from 'supertest';
84
+
85
+ describe('API Integration Tests', () => {
86
+ beforeAll(async () => {
87
+ await setupTestDatabase();
88
+ });
89
+
90
+ afterAll(async () => {
91
+ await teardownTestDatabase();
92
+ });
93
+
94
+ beforeEach(async () => {
95
+ await clearTestData();
96
+ });
97
+
98
+ describe('POST /api/users', () => {
99
+ it('should create a new user', async () => {
100
+ const response = await request(app)
101
+ .post('/api/users')
102
+ .send({
103
+ email: 'test@example.com',
104
+ name: 'Test User'
105
+ });
106
+
107
+ expect(response.status).toBe(201);
108
+ expect(response.body).toMatchObject({
109
+ email: 'test@example.com',
110
+ name: 'Test User'
111
+ });
112
+ expect(response.body.id).toBeDefined();
113
+ });
114
+
115
+ it('should return 400 for invalid email', async () => {
116
+ const response = await request(app)
117
+ .post('/api/users')
118
+ .send({
119
+ email: 'invalid-email',
120
+ name: 'Test'
121
+ });
122
+
123
+ expect(response.status).toBe(400);
124
+ expect(response.body.error).toContain('email');
125
+ });
126
+ });
127
+
128
+ describe('GET /api/users/:id', () => {
129
+ it('should return user by id', async () => {
130
+ // First create a user
131
+ const created = await request(app)
132
+ .post('/api/users')
133
+ .send({ email: 'test@test.com', name: 'Test' });
134
+
135
+ const response = await request(app)
136
+ .get(`/api/users/${created.body.id}`);
137
+
138
+ expect(response.status).toBe(200);
139
+ expect(response.body.email).toBe('test@test.com');
140
+ });
141
+
142
+ it('should return 404 for non-existent user', async () => {
143
+ const response = await request(app)
144
+ .get('/api/users/999999');
145
+
146
+ expect(response.status).toBe(404);
147
+ });
148
+ });
149
+ });
150
+ ```
151
+
152
+ ### 3. E2E Tests with Playwright
153
+
154
+ ```typescript
155
+ import { test, expect } from '@playwright/test';
156
+
157
+ test.describe('User Registration Flow', () => {
158
+ test('should register a new user', async ({ page }) => {
159
+ await page.goto('/register');
160
+
161
+ await page.fill('[data-testid="email"]', 'newuser@example.com');
162
+ await page.fill('[data-testid="password"]', 'password123');
163
+ await page.fill('[data-testid="confirm-password"]', 'password123');
164
+ await page.click('[data-testid="register-button"]');
165
+
166
+ // Should redirect to dashboard
167
+ await expect(page).toHaveURL('/dashboard');
168
+
169
+ // Should show success message
170
+ await expect(page.locator('.toast')).toContainText('Welcome');
171
+ });
172
+
173
+ test('should show validation errors', async ({ page }) => {
174
+ await page.goto('/register');
175
+
176
+ await page.fill('[data-testid="email"]', 'invalid');
177
+ await page.click('[data-testid="register-button"]');
178
+
179
+ await expect(page.locator('[data-testid="email-error"]'))
180
+ .toContainText('Valid email required');
181
+ });
182
+ });
183
+
184
+ test.describe('Authentication', () => {
185
+ test.beforeEach(async ({ page }) => {
186
+ // Login before each test
187
+ await page.goto('/login');
188
+ await page.fill('[data-testid="email"]', 'test@example.com');
189
+ await page.fill('[data-testid="password"]', 'password123');
190
+ await page.click('[data-testid="login-button"]');
191
+ });
192
+
193
+ test('should protect dashboard', async ({ page }) => {
194
+ await page.goto('/dashboard');
195
+ await expect(page).toHaveURL('/dashboard');
196
+ });
197
+ });
198
+ ```
199
+
200
+ ### 4. Test Fixtures
201
+
202
+ ```typescript
203
+ import { test as base, fixtures } from '@playwright/test';
204
+
205
+ interface UserFixtures {
206
+ testUser: { email: string; password: string };
207
+ adminUser: { email: string; role: string };
208
+ }
209
+
210
+ const myFixtures = fixtures({
211
+ testUser: async ({}, use) => {
212
+ const user = {
213
+ email: 'test@example.com',
214
+ password: 'testpass123'
215
+ };
216
+ await createTestUser(user);
217
+ await use(user);
218
+ await deleteTestUser(user.email);
219
+ },
220
+
221
+ adminUser: async ({}, use) => {
222
+ const user = {
223
+ email: 'admin@example.com',
224
+ role: 'admin'
225
+ };
226
+ await createAdminUser(user);
227
+ await use(user);
228
+ await deleteTestUser(user.email);
229
+ }
230
+ });
231
+
232
+ export const test = base.extend<UserFixtures>(myFixtures);
233
+ ```
234
+
235
+ ### 5. Mocking External Services
236
+
237
+ ```typescript
238
+ import { vi } from 'vitest';
239
+ import nock from 'nock';
240
+
241
+ describe('PaymentService', () => {
242
+ afterEach(() => {
243
+ nock.cleanAll();
244
+ });
245
+
246
+ it('should process payment via Stripe', async () => {
247
+ // Mock Stripe API
248
+ nock('https://api.stripe.com')
249
+ .post('/v1/charges')
250
+ .reply(200, {
251
+ id: 'ch_123',
252
+ status: 'succeeded',
253
+ amount: 1000
254
+ });
255
+
256
+ const service = new PaymentService();
257
+ const result = await service.processPayment({
258
+ amount: 1000,
259
+ currency: 'usd',
260
+ token: 'tok_visa'
261
+ });
262
+
263
+ expect(result.status).toBe('succeeded');
264
+ });
265
+
266
+ it('should handle payment failures', async () => {
267
+ nock('https://api.stripe.com')
268
+ .post('/v1/charges')
269
+ .reply(402, {
270
+ error: {
271
+ code: 'card_declined',
272
+ message: 'Card was declined'
273
+ }
274
+ });
275
+
276
+ const service = new PaymentService();
277
+
278
+ await expect(service.processPayment({
279
+ amount: 1000,
280
+ currency: 'usd',
281
+ token: 'tok_chargeDeclined'
282
+ })).rejects.toThrow('Payment failed');
283
+ });
284
+ });
285
+ ```
286
+
287
+ ### 6. Test Coverage
288
+
289
+ ```typescript
290
+ // vitest.config.ts
291
+ import { defineConfig } from 'vitest/config';
292
+
293
+ export default defineConfig({
294
+ test: {
295
+ coverage: {
296
+ provider: 'v8',
297
+ reporter: ['text', 'json', 'html'],
298
+ include: [
299
+ 'src/**/*.ts',
300
+ '!src/**/*.d.ts'
301
+ ],
302
+ exclude: [
303
+ 'src/**/*.test.ts',
304
+ 'src/**/*.spec.ts'
305
+ ]
306
+ }
307
+ }
308
+ });
309
+
310
+ // Run with coverage
311
+ // npm run test:coverage
312
+ ```
313
+
314
+ ## Best Practices
315
+
316
+ 1. **AAA Pattern** - Arrange, Act, Assert
317
+ 2. **One assertion per test** - Better failure messages
318
+ 3. **Test behavior, not implementation** - Refactor safe
319
+ 4. **Descriptive names** - Document what's tested
320
+ 5. **Fast tests** - No network calls in unit tests
321
+ 6. **Isolation** - Tests don't depend on each other
322
+ 7. **Mocks external services** - Speed and reliability
323
+
324
+ ## Common Mistakes
325
+
326
+ - Testing implementation details
327
+ - Not cleaning up test data
328
+ - Over-mocking
329
+ - Slow test suites
330
+ - Brittle selectors
331
+ - Not testing edge cases
332
+ - Skipping E2E tests