qa-skills 3.0.0

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 (262) hide show
  1. package/README.md +168 -0
  2. package/bin/cli.js +42 -0
  3. package/dist/agents/registry.d.ts +5 -0
  4. package/dist/agents/registry.d.ts.map +1 -0
  5. package/dist/agents/registry.js +101 -0
  6. package/dist/agents/registry.js.map +1 -0
  7. package/dist/agents/types.d.ts +9 -0
  8. package/dist/agents/types.d.ts.map +1 -0
  9. package/dist/agents/types.js +2 -0
  10. package/dist/agents/types.js.map +1 -0
  11. package/dist/dependencies.d.ts +21 -0
  12. package/dist/dependencies.d.ts.map +1 -0
  13. package/dist/dependencies.js +125 -0
  14. package/dist/dependencies.js.map +1 -0
  15. package/dist/installer.d.ts +25 -0
  16. package/dist/installer.d.ts.map +1 -0
  17. package/dist/installer.js +437 -0
  18. package/dist/installer.js.map +1 -0
  19. package/dist/scaffold.d.ts +27 -0
  20. package/dist/scaffold.d.ts.map +1 -0
  21. package/dist/scaffold.js +182 -0
  22. package/dist/scaffold.js.map +1 -0
  23. package/package.json +40 -0
  24. package/skills/qa-accessibility-test-writer/SKILL.md +127 -0
  25. package/skills/qa-accessibility-test-writer/references/axe-core-patterns.md +349 -0
  26. package/skills/qa-accessibility-test-writer/references/best-practices.md +184 -0
  27. package/skills/qa-accessibility-test-writer/references/wcag-tests.md +331 -0
  28. package/skills/qa-api-contract-curator/SKILL.md +104 -0
  29. package/skills/qa-api-contract-curator/references/breaking-changes.md +363 -0
  30. package/skills/qa-api-contract-curator/references/openapi-structure.md +404 -0
  31. package/skills/qa-browser-data-collector/SKILL.md +132 -0
  32. package/skills/qa-browser-data-collector/references/data-collection-checklist.md +91 -0
  33. package/skills/qa-browser-data-collector/references/playwright-mcp-patterns.md +113 -0
  34. package/skills/qa-bug-ticket-creator/SKILL.md +148 -0
  35. package/skills/qa-bug-ticket-creator/references/bug-report-format.md +149 -0
  36. package/skills/qa-bug-ticket-creator/references/severity-guide.md +81 -0
  37. package/skills/qa-bug-ticket-creator/templates/bug-ticket-template.md +39 -0
  38. package/skills/qa-changelog-analyzer/SKILL.md +134 -0
  39. package/skills/qa-changelog-analyzer/references/git-analysis-patterns.md +138 -0
  40. package/skills/qa-changelog-analyzer/references/impact-mapping.md +120 -0
  41. package/skills/qa-clickup-integration/SKILL.md +166 -0
  42. package/skills/qa-clickup-integration/references/api-patterns.md +102 -0
  43. package/skills/qa-clickup-integration/references/field-mapping.md +71 -0
  44. package/skills/qa-codeceptjs-writer/SKILL.md +136 -0
  45. package/skills/qa-codeceptjs-writer/references/best-practices.md +207 -0
  46. package/skills/qa-codeceptjs-writer/references/config.md +255 -0
  47. package/skills/qa-codeceptjs-writer/references/patterns.md +285 -0
  48. package/skills/qa-coverage-analyzer/SKILL.md +166 -0
  49. package/skills/qa-coverage-analyzer/references/best-practices.md +142 -0
  50. package/skills/qa-coverage-analyzer/references/coverage-dimensions.md +155 -0
  51. package/skills/qa-coverage-analyzer/references/tools.md +204 -0
  52. package/skills/qa-cypress-writer/SKILL.md +134 -0
  53. package/skills/qa-cypress-writer/references/assertions.md +121 -0
  54. package/skills/qa-cypress-writer/references/best-practices.md +82 -0
  55. package/skills/qa-cypress-writer/references/config.md +121 -0
  56. package/skills/qa-cypress-writer/references/patterns.md +170 -0
  57. package/skills/qa-data-factory/SKILL.md +126 -0
  58. package/skills/qa-data-factory/references/factory-patterns.md +164 -0
  59. package/skills/qa-data-factory/references/faker-guide.md +131 -0
  60. package/skills/qa-diagram-generator/SKILL.md +125 -0
  61. package/skills/qa-diagram-generator/references/c4-model.md +53 -0
  62. package/skills/qa-diagram-generator/references/charts.md +58 -0
  63. package/skills/qa-diagram-generator/references/class-diagram.md +85 -0
  64. package/skills/qa-diagram-generator/references/er-diagram.md +69 -0
  65. package/skills/qa-diagram-generator/references/flowchart.md +92 -0
  66. package/skills/qa-diagram-generator/references/from-screenshot.md +45 -0
  67. package/skills/qa-diagram-generator/references/gantt.md +49 -0
  68. package/skills/qa-diagram-generator/references/journey.md +50 -0
  69. package/skills/qa-diagram-generator/references/mindmap.md +75 -0
  70. package/skills/qa-diagram-generator/references/sequence.md +69 -0
  71. package/skills/qa-diagram-generator/references/state-diagram.md +56 -0
  72. package/skills/qa-discovery-interview/SKILL.md +182 -0
  73. package/skills/qa-discovery-interview/references/completeness-checklist.md +53 -0
  74. package/skills/qa-discovery-interview/references/conflict-patterns.md +101 -0
  75. package/skills/qa-discovery-interview/references/qa-categories.md +147 -0
  76. package/skills/qa-discovery-interview/templates/qa-brief-template.md +168 -0
  77. package/skills/qa-environment-checker/SKILL.md +142 -0
  78. package/skills/qa-environment-checker/references/dependency-matrix.md +101 -0
  79. package/skills/qa-environment-checker/references/health-checks.md +209 -0
  80. package/skills/qa-environment-checker/templates/env-readiness-template.md +64 -0
  81. package/skills/qa-flaky-detector/SKILL.md +153 -0
  82. package/skills/qa-flaky-detector/references/ci-analysis.md +140 -0
  83. package/skills/qa-flaky-detector/references/flaky-patterns.md +247 -0
  84. package/skills/qa-github-issues-enhanced/SKILL.md +175 -0
  85. package/skills/qa-github-issues-enhanced/references/issue-templates.md +425 -0
  86. package/skills/qa-github-issues-enhanced/references/label-taxonomy.md +130 -0
  87. package/skills/qa-github-issues-enhanced/references/workflow-patterns.md +188 -0
  88. package/skills/qa-httpx-writer/SKILL.md +138 -0
  89. package/skills/qa-httpx-writer/references/assertions.md +195 -0
  90. package/skills/qa-httpx-writer/references/best-practices.md +140 -0
  91. package/skills/qa-httpx-writer/references/config.md +212 -0
  92. package/skills/qa-httpx-writer/references/patterns.md +262 -0
  93. package/skills/qa-jest-writer/SKILL.md +131 -0
  94. package/skills/qa-jest-writer/references/assertions.md +125 -0
  95. package/skills/qa-jest-writer/references/best-practices.md +136 -0
  96. package/skills/qa-jest-writer/references/config.md +134 -0
  97. package/skills/qa-jest-writer/references/patterns.md +172 -0
  98. package/skills/qa-jira-integration/SKILL.md +135 -0
  99. package/skills/qa-jira-integration/references/api-patterns.md +143 -0
  100. package/skills/qa-jira-integration/references/field-mapping.md +79 -0
  101. package/skills/qa-jira-integration/references/xray-integration.md +85 -0
  102. package/skills/qa-jmeter-writer/SKILL.md +171 -0
  103. package/skills/qa-jmeter-writer/references/best-practices.md +157 -0
  104. package/skills/qa-jmeter-writer/references/config.md +204 -0
  105. package/skills/qa-jmeter-writer/references/patterns.md +242 -0
  106. package/skills/qa-junit5-writer/SKILL.md +157 -0
  107. package/skills/qa-junit5-writer/references/assertions.md +118 -0
  108. package/skills/qa-junit5-writer/references/config.md +97 -0
  109. package/skills/qa-junit5-writer/references/patterns.md +162 -0
  110. package/skills/qa-k6-writer/SKILL.md +155 -0
  111. package/skills/qa-k6-writer/references/best-practices.md +236 -0
  112. package/skills/qa-k6-writer/references/config.md +219 -0
  113. package/skills/qa-k6-writer/references/patterns.md +304 -0
  114. package/skills/qa-linear-integration/SKILL.md +137 -0
  115. package/skills/qa-linear-integration/references/api-patterns.md +249 -0
  116. package/skills/qa-linear-integration/references/field-mapping.md +121 -0
  117. package/skills/qa-locust-writer/SKILL.md +151 -0
  118. package/skills/qa-locust-writer/references/best-practices.md +126 -0
  119. package/skills/qa-locust-writer/references/config.md +170 -0
  120. package/skills/qa-locust-writer/references/patterns.md +235 -0
  121. package/skills/qa-manual-test-designer/SKILL.md +145 -0
  122. package/skills/qa-manual-test-designer/references/exploratory-charters.md +138 -0
  123. package/skills/qa-manual-test-designer/references/personas.md +146 -0
  124. package/skills/qa-manual-test-designer/templates/exploratory-charter-template.md +47 -0
  125. package/skills/qa-manual-test-designer/templates/test-case-template.md +31 -0
  126. package/skills/qa-mobile-test-writer/SKILL.md +144 -0
  127. package/skills/qa-mobile-test-writer/references/best-practices.md +214 -0
  128. package/skills/qa-mobile-test-writer/references/config.md +309 -0
  129. package/skills/qa-mobile-test-writer/references/patterns.md +304 -0
  130. package/skills/qa-nfr-analyst/SKILL.md +177 -0
  131. package/skills/qa-nfr-analyst/references/iso-25010-model.md +159 -0
  132. package/skills/qa-nfr-analyst/references/owasp-wstg-baseline.md +202 -0
  133. package/skills/qa-nfr-analyst/references/wcag-checklist.md +184 -0
  134. package/skills/qa-nfr-analyst/templates/owasp-checklist-template.md +89 -0
  135. package/skills/qa-nfr-analyst/templates/wcag-checklist-template.md +48 -0
  136. package/skills/qa-orchestrator/SKILL.md +132 -0
  137. package/skills/qa-orchestrator/references/handoff-chains.md +105 -0
  138. package/skills/qa-orchestrator/references/pipeline-modes.md +115 -0
  139. package/skills/qa-orchestrator/references/scheduler-rules.md +84 -0
  140. package/skills/qa-pact-writer/SKILL.md +133 -0
  141. package/skills/qa-pact-writer/references/best-practices.md +100 -0
  142. package/skills/qa-pact-writer/references/config.md +135 -0
  143. package/skills/qa-pact-writer/references/patterns.md +161 -0
  144. package/skills/qa-plan-creator/SKILL.md +139 -0
  145. package/skills/qa-plan-creator/references/introduction-plan.md +43 -0
  146. package/skills/qa-plan-creator/references/migration-plan.md +44 -0
  147. package/skills/qa-plan-creator/references/onboarding-plan.md +46 -0
  148. package/skills/qa-plan-creator/references/performance-plan.md +44 -0
  149. package/skills/qa-plan-creator/references/regression-plan.md +45 -0
  150. package/skills/qa-plan-creator/references/release-plan.md +45 -0
  151. package/skills/qa-plan-creator/references/sprint-plan.md +44 -0
  152. package/skills/qa-plan-creator/references/test-plan.md +59 -0
  153. package/skills/qa-plan-creator/references/uat-plan.md +43 -0
  154. package/skills/qa-plan-creator/templates/checklist-template.md +36 -0
  155. package/skills/qa-plan-creator/templates/regression-checklist-template.md +49 -0
  156. package/skills/qa-plan-creator/templates/release-checklist-template.md +46 -0
  157. package/skills/qa-plan-creator/templates/test-plan-template.md +74 -0
  158. package/skills/qa-playwright-py-writer/SKILL.md +156 -0
  159. package/skills/qa-playwright-py-writer/references/best-practices.md +194 -0
  160. package/skills/qa-playwright-py-writer/references/config.md +195 -0
  161. package/skills/qa-playwright-py-writer/references/patterns.md +212 -0
  162. package/skills/qa-playwright-ts-writer/SKILL.md +151 -0
  163. package/skills/qa-playwright-ts-writer/references/assertions.md +109 -0
  164. package/skills/qa-playwright-ts-writer/references/best-practices.md +191 -0
  165. package/skills/qa-playwright-ts-writer/references/config.md +144 -0
  166. package/skills/qa-playwright-ts-writer/references/patterns.md +171 -0
  167. package/skills/qa-pytest-writer/SKILL.md +145 -0
  168. package/skills/qa-pytest-writer/references/assertions.md +149 -0
  169. package/skills/qa-pytest-writer/references/best-practices.md +97 -0
  170. package/skills/qa-pytest-writer/references/config.md +176 -0
  171. package/skills/qa-pytest-writer/references/patterns.md +251 -0
  172. package/skills/qa-qase-integration/SKILL.md +149 -0
  173. package/skills/qa-qase-integration/references/api-reference.md +354 -0
  174. package/skills/qa-qase-integration/references/ci-integration.md +196 -0
  175. package/skills/qa-qase-integration/references/field-mapping.md +157 -0
  176. package/skills/qa-requirements-generator/SKILL.md +152 -0
  177. package/skills/qa-requirements-generator/references/iso-29148-structure.md +153 -0
  178. package/skills/qa-requirements-generator/references/requirement-patterns.md +278 -0
  179. package/skills/qa-rest-assured-writer/SKILL.md +137 -0
  180. package/skills/qa-rest-assured-writer/references/best-practices.md +50 -0
  181. package/skills/qa-rest-assured-writer/references/config.md +124 -0
  182. package/skills/qa-rest-assured-writer/references/patterns.md +192 -0
  183. package/skills/qa-risk-analyzer/SKILL.md +158 -0
  184. package/skills/qa-risk-analyzer/references/impact-analysis.md +133 -0
  185. package/skills/qa-risk-analyzer/references/risk-factors.md +123 -0
  186. package/skills/qa-robot-framework-writer/SKILL.md +147 -0
  187. package/skills/qa-robot-framework-writer/references/best-practices.md +249 -0
  188. package/skills/qa-robot-framework-writer/references/config.md +204 -0
  189. package/skills/qa-robot-framework-writer/references/libraries.md +273 -0
  190. package/skills/qa-robot-framework-writer/references/patterns.md +216 -0
  191. package/skills/qa-security-test-writer/SKILL.md +123 -0
  192. package/skills/qa-security-test-writer/references/best-practices.md +155 -0
  193. package/skills/qa-security-test-writer/references/owasp-top10.md +331 -0
  194. package/skills/qa-security-test-writer/references/zap-config.md +258 -0
  195. package/skills/qa-selenium-java-writer/SKILL.md +143 -0
  196. package/skills/qa-selenium-java-writer/references/best-practices.md +59 -0
  197. package/skills/qa-selenium-java-writer/references/config.md +143 -0
  198. package/skills/qa-selenium-java-writer/references/patterns.md +170 -0
  199. package/skills/qa-selenium-py-writer/SKILL.md +150 -0
  200. package/skills/qa-selenium-py-writer/references/best-practices.md +175 -0
  201. package/skills/qa-selenium-py-writer/references/config.md +224 -0
  202. package/skills/qa-selenium-py-writer/references/patterns.md +255 -0
  203. package/skills/qa-shortcut-integration/SKILL.md +143 -0
  204. package/skills/qa-shortcut-integration/references/api-patterns.md +126 -0
  205. package/skills/qa-shortcut-integration/references/field-mapping.md +66 -0
  206. package/skills/qa-spec-auditor/SKILL.md +162 -0
  207. package/skills/qa-spec-auditor/references/audit-checklist.md +144 -0
  208. package/skills/qa-spec-auditor/references/drift-patterns.md +207 -0
  209. package/skills/qa-spec-writer/SKILL.md +143 -0
  210. package/skills/qa-spec-writer/references/gherkin-guide.md +253 -0
  211. package/skills/qa-spec-writer/references/specification-patterns.md +274 -0
  212. package/skills/qa-spring-test-writer/SKILL.md +170 -0
  213. package/skills/qa-spring-test-writer/references/best-practices.md +57 -0
  214. package/skills/qa-spring-test-writer/references/config.md +179 -0
  215. package/skills/qa-spring-test-writer/references/patterns.md +235 -0
  216. package/skills/qa-supertest-writer/SKILL.md +150 -0
  217. package/skills/qa-supertest-writer/references/assertions.md +192 -0
  218. package/skills/qa-supertest-writer/references/best-practices.md +102 -0
  219. package/skills/qa-supertest-writer/references/config.md +166 -0
  220. package/skills/qa-supertest-writer/references/patterns.md +242 -0
  221. package/skills/qa-task-creator/SKILL.md +142 -0
  222. package/skills/qa-task-creator/references/linking-patterns.md +127 -0
  223. package/skills/qa-task-creator/references/task-types.md +169 -0
  224. package/skills/qa-task-creator/templates/task-template.md +24 -0
  225. package/skills/qa-test-doc-compiler/SKILL.md +114 -0
  226. package/skills/qa-test-doc-compiler/references/agile-tailoring.md +220 -0
  227. package/skills/qa-test-doc-compiler/references/iso-29119-3-documents.md +302 -0
  228. package/skills/qa-test-healer/SKILL.md +101 -0
  229. package/skills/qa-test-healer/references/diagnosis-patterns.md +142 -0
  230. package/skills/qa-test-healer/references/fix-strategies.md +177 -0
  231. package/skills/qa-test-reporter/SKILL.md +130 -0
  232. package/skills/qa-test-reporter/references/best-practices.md +162 -0
  233. package/skills/qa-test-reporter/references/iso-29119-reports.md +236 -0
  234. package/skills/qa-test-reporter/references/report-formats.md +287 -0
  235. package/skills/qa-test-reviewer/SKILL.md +142 -0
  236. package/skills/qa-test-reviewer/references/anti-patterns.md +268 -0
  237. package/skills/qa-test-reviewer/references/review-checklist.md +93 -0
  238. package/skills/qa-test-strategy/SKILL.md +133 -0
  239. package/skills/qa-test-strategy/references/entry-exit-criteria.md +176 -0
  240. package/skills/qa-test-strategy/references/risk-matrix.md +102 -0
  241. package/skills/qa-test-strategy/references/testing-types.md +143 -0
  242. package/skills/qa-testcase-from-docs/SKILL.md +161 -0
  243. package/skills/qa-testcase-from-docs/references/test-case-format.md +196 -0
  244. package/skills/qa-testcase-from-docs/references/test-design-techniques.md +126 -0
  245. package/skills/qa-testcase-from-docs/templates/test-case-template.md +31 -0
  246. package/skills/qa-testcase-from-ui/SKILL.md +109 -0
  247. package/skills/qa-testcase-from-ui/references/ui-element-patterns.md +126 -0
  248. package/skills/qa-testcase-from-ui/references/visual-analysis-guide.md +146 -0
  249. package/skills/qa-testcase-from-ui/templates/test-case-template.md +31 -0
  250. package/skills/qa-visual-regression-writer/SKILL.md +175 -0
  251. package/skills/qa-visual-regression-writer/references/best-practices.md +154 -0
  252. package/skills/qa-visual-regression-writer/references/config.md +220 -0
  253. package/skills/qa-visual-regression-writer/references/patterns.md +213 -0
  254. package/skills/qa-vitest-writer/SKILL.md +141 -0
  255. package/skills/qa-vitest-writer/references/assertions.md +105 -0
  256. package/skills/qa-vitest-writer/references/best-practices.md +62 -0
  257. package/skills/qa-vitest-writer/references/config.md +127 -0
  258. package/skills/qa-vitest-writer/references/patterns.md +141 -0
  259. package/skills/qa-webdriverio-writer/SKILL.md +145 -0
  260. package/skills/qa-webdriverio-writer/references/best-practices.md +176 -0
  261. package/skills/qa-webdriverio-writer/references/config.md +240 -0
  262. package/skills/qa-webdriverio-writer/references/patterns.md +269 -0
@@ -0,0 +1,140 @@
1
+ # Python API Testing Best Practices
2
+
3
+ ## Async vs Sync
4
+
5
+ | Use Case | Recommendation |
6
+ | -------- | -------------- |
7
+ | **Most API tests** | Sync `httpx.Client` — simpler, sufficient for typical CRUD |
8
+ | **High concurrency** | Async `httpx.AsyncClient` — when testing many parallel requests |
9
+ | **Async app under test** | Async client — matches async handlers |
10
+ | **Mixed codebase** | Prefer sync unless async is required |
11
+
12
+ ### When to Use Async
13
+
14
+ - Testing endpoints that benefit from concurrent requests
15
+ - Matching async application architecture
16
+ - Performance benchmarks with many simultaneous calls
17
+
18
+ ### Sync Simplicity
19
+
20
+ ```python
21
+ # Prefer sync for straightforward tests
22
+ def test_get_users(client, base_url):
23
+ response = client.get(f"{base_url}/users")
24
+ assert response.status_code == 200
25
+ ```
26
+
27
+ ## Session Reuse
28
+
29
+ | Scope | Pros | Cons |
30
+ | ----- | ---- | ---- |
31
+ | **Function** | Isolated; no shared state | New connection per test |
32
+ | **Module** | Fewer connections | Shared state across tests in module |
33
+ | **Session** | Fastest; single connection pool | Must avoid mutating shared state |
34
+
35
+ **Recommendation:** Use function-scoped client by default. Use session scope only when tests are read-only and do not mutate server state.
36
+
37
+ ## Schema Validation
38
+
39
+ 1. **Use Pydantic when** — OpenAPI contract exists; response shape is well-defined
40
+ 2. **Use JSON Schema when** — Contract is in JSON Schema format; no Pydantic in project
41
+ 3. **Use manual assertions when** — Only a few fields matter; full schema is overkill
42
+
43
+ ### Contract-Driven
44
+
45
+ ```python
46
+ # From qa-api-contract-curator OpenAPI spec
47
+ class UserResponse(BaseModel):
48
+ id: int
49
+ email: EmailStr
50
+ name: str | None = None
51
+
52
+ def test_user_matches_contract(client, base_url, auth_headers):
53
+ response = client.get(f"{base_url}/users/1", headers=auth_headers)
54
+ UserResponse.model_validate(response.json()) # Fails if contract violated
55
+ ```
56
+
57
+ ## Test Isolation
58
+
59
+ 1. **Independent tests** — Each test passes regardless of order
60
+ 2. **No shared mutable state** — Avoid global variables; use fixtures
61
+ 3. **Clean data** — Create/delete test data per test or use unique identifiers
62
+ 4. **Idempotent when possible** — GET requests are safe; POST may create duplicates — use unique emails, UUIDs
63
+
64
+ ### Unique Test Data
65
+
66
+ ```python
67
+ import uuid
68
+
69
+ def test_create_user_unique(client, base_url, auth_headers):
70
+ email = f"test-{uuid.uuid4().hex}@example.com"
71
+ response = client.post(
72
+ f"{base_url}/users",
73
+ json={"email": email, "name": "Test"},
74
+ headers=auth_headers
75
+ )
76
+ assert response.status_code == 201
77
+ ```
78
+
79
+ ## Error Scenarios
80
+
81
+ Cover at least:
82
+
83
+ | Scenario | Status | Assertion |
84
+ | -------- | ------ | --------- |
85
+ | Success | 200/201/204 | Per contract |
86
+ | Validation error | 400 | Error payload structure |
87
+ | Unauthorized | 401 | When missing/invalid auth |
88
+ | Forbidden | 403 | Insufficient permissions |
89
+ | Not found | 404 | Missing resource |
90
+ | Conflict | 409 | Duplicate, constraint violation |
91
+
92
+ ## Avoid
93
+
94
+ 1. **Hardcoded secrets** — Use `os.environ` or pytest fixtures
95
+ 2. **Fragile assertions** — Prefer partial match for dynamic fields (id, createdAt)
96
+ 3. **Sleep/timeout for flakiness** — Use retries or deterministic data instead
97
+ 4. **Testing implementation** — Test behavior and contract, not internal routes
98
+ 5. **Large fixtures** — Minimize payload size; use factories for scale
99
+ 6. **External services in unit tests** — Mock third-party APIs; use real API only in integration tests
100
+
101
+ ## File Organization
102
+
103
+ ```
104
+ tests/
105
+ conftest.py # base_url, client, auth
106
+ api/
107
+ conftest.py # auth_headers, token
108
+ test_users_api.py
109
+ test_products_api.py
110
+ unit/ # Mocked API calls
111
+ test_services.py
112
+ ```
113
+
114
+ ## Parallel Execution
115
+
116
+ When using `pytest-xdist`:
117
+
118
+ - Use unique data (UUIDs, timestamps) to avoid collisions
119
+ - Or run API tests sequentially: `pytest -m api --forked` or single worker
120
+ - Consider separate DB/schema per worker for integration tests
121
+
122
+ ## Dependencies
123
+
124
+ ```toml
125
+ # pyproject.toml
126
+ [project.optional-dependencies]
127
+ test = [
128
+ "httpx>=0.24.0",
129
+ "pytest>=7.0",
130
+ "pytest-asyncio>=0.21.0",
131
+ "pydantic>=2.0",
132
+ ]
133
+ ```
134
+
135
+ ## Contract-Driven Workflow
136
+
137
+ 1. **Get OpenAPI** — From qa-api-contract-curator
138
+ 2. **Generate Pydantic models** — From `components.schemas` (or use datamodel-code-generator)
139
+ 3. **Generate tests** — One test per operation × status code
140
+ 4. **Validate responses** — `Model.model_validate(response.json())`
@@ -0,0 +1,212 @@
1
+ # httpx Test Configuration
2
+
3
+ ## conftest.py Structure
4
+
5
+ ```python
6
+ # tests/conftest.py
7
+ import os
8
+ import pytest
9
+ import httpx
10
+
11
+
12
+ @pytest.fixture
13
+ def base_url():
14
+ return os.environ.get("API_BASE_URL", "http://localhost:8000")
15
+
16
+
17
+ @pytest.fixture
18
+ def client(base_url):
19
+ """Sync client; use for non-async tests."""
20
+ with httpx.Client(
21
+ base_url=base_url,
22
+ timeout=30.0,
23
+ follow_redirects=True,
24
+ ) as c:
25
+ yield c
26
+
27
+
28
+ @pytest.fixture
29
+ async def async_client(base_url):
30
+ """Async client; use with @pytest.mark.asyncio."""
31
+ async with httpx.AsyncClient(
32
+ base_url=base_url,
33
+ timeout=30.0,
34
+ follow_redirects=True,
35
+ ) as c:
36
+ yield c
37
+ ```
38
+
39
+ ## Base URL Management
40
+
41
+ ### Environment Variable
42
+
43
+ ```python
44
+ # .env.test or pytest.ini
45
+ # API_BASE_URL=https://api.staging.example.com
46
+
47
+ @pytest.fixture
48
+ def base_url():
49
+ url = os.environ.get("API_BASE_URL")
50
+ if not url:
51
+ pytest.skip("API_BASE_URL not set")
52
+ return url
53
+ ```
54
+
55
+ ### Per-Environment
56
+
57
+ ```python
58
+ @pytest.fixture
59
+ def base_url():
60
+ env = os.environ.get("TEST_ENV", "local")
61
+ urls = {
62
+ "local": "http://localhost:8000",
63
+ "staging": "https://api.staging.example.com",
64
+ "integration": "https://api.integration.example.com",
65
+ }
66
+ return urls.get(env, urls["local"])
67
+ ```
68
+
69
+ ## Client Fixtures
70
+
71
+ ### Function-Scoped (Default)
72
+
73
+ ```python
74
+ @pytest.fixture
75
+ def client(base_url):
76
+ with httpx.Client(base_url=base_url) as c:
77
+ yield c
78
+ # New client per test; no shared state
79
+ ```
80
+
81
+ ### Session-Scoped (Connection Reuse)
82
+
83
+ ```python
84
+ @pytest.fixture(scope="session")
85
+ def client(base_url):
86
+ with httpx.Client(base_url=base_url) as c:
87
+ yield c
88
+ # Reuse connection across tests; faster but shared state
89
+ ```
90
+
91
+ ### With Custom Headers
92
+
93
+ ```python
94
+ @pytest.fixture
95
+ def client(base_url, default_headers):
96
+ with httpx.Client(
97
+ base_url=base_url,
98
+ headers=default_headers,
99
+ ) as c:
100
+ yield c
101
+
102
+ @pytest.fixture
103
+ def default_headers():
104
+ return {
105
+ "Accept": "application/json",
106
+ "Content-Type": "application/json",
107
+ "User-Agent": "qa-httpx-tests/1.0",
108
+ }
109
+ ```
110
+
111
+ ## Auth Fixtures
112
+
113
+ ```python
114
+ @pytest.fixture
115
+ def token(client, base_url):
116
+ response = client.post(
117
+ f"{base_url}/auth/login",
118
+ json={
119
+ "email": os.environ.get("TEST_USER_EMAIL", "test@example.com"),
120
+ "password": os.environ.get("TEST_USER_PASSWORD", "test"),
121
+ },
122
+ )
123
+ assert response.status_code == 200
124
+ return response.json()["token"]
125
+
126
+
127
+ @pytest.fixture
128
+ def auth_headers(token):
129
+ return {"Authorization": f"Bearer {token}"}
130
+
131
+
132
+ @pytest.fixture
133
+ def auth_client(client, auth_headers):
134
+ client.headers.update(auth_headers)
135
+ return client
136
+ ```
137
+
138
+ ## pytest-asyncio Configuration
139
+
140
+ ### pyproject.toml
141
+
142
+ ```toml
143
+ [tool.pytest.ini_options]
144
+ asyncio_mode = "auto"
145
+ asyncio_default_fixture_loop_scope = "function"
146
+ testpaths = ["tests"]
147
+ ```
148
+
149
+ ### pytest.ini
150
+
151
+ ```ini
152
+ [pytest]
153
+ asyncio_mode = auto
154
+ asyncio_default_fixture_loop_scope = function
155
+ ```
156
+
157
+ ## Timeout and Retry
158
+
159
+ ```python
160
+ from httpx import Timeout, Limits
161
+
162
+ @pytest.fixture
163
+ def client(base_url):
164
+ with httpx.Client(
165
+ base_url=base_url,
166
+ timeout=Timeout(60.0),
167
+ limits=Limits(max_keepalive_connections=5, max_connections=10),
168
+ ) as c:
169
+ yield c
170
+ ```
171
+
172
+ ## Markers for API Tests
173
+
174
+ ```toml
175
+ # pyproject.toml
176
+ [tool.pytest.ini_options]
177
+ markers = [
178
+ "api: marks tests as API integration tests",
179
+ "slow: marks tests as slow (network-dependent)",
180
+ ]
181
+ ```
182
+
183
+ ```python
184
+ @pytest.mark.api
185
+ def test_users_endpoint(client, base_url):
186
+ response = client.get(f"{base_url}/users")
187
+ assert response.status_code == 200
188
+ ```
189
+
190
+ ## Directory Layout
191
+
192
+ ```
193
+ tests/
194
+ conftest.py # Shared fixtures
195
+ api/
196
+ conftest.py # API-specific fixtures (auth, etc.)
197
+ test_users_api.py
198
+ test_products_api.py
199
+ test_auth_api.py
200
+ fixtures/
201
+ sample_users.json
202
+ ```
203
+
204
+ ## Environment Config
205
+
206
+ ```python
207
+ # tests/conftest.py
208
+ def pytest_configure(config):
209
+ os.environ.setdefault("TESTING", "1")
210
+ if "API_BASE_URL" not in os.environ:
211
+ os.environ["API_BASE_URL"] = "http://localhost:8000"
212
+ ```
@@ -0,0 +1,262 @@
1
+ # httpx API Test Patterns
2
+
3
+ ## Sync Client (httpx.Client)
4
+
5
+ ```python
6
+ import httpx
7
+
8
+ def test_get_users():
9
+ with httpx.Client(base_url="https://api.example.com") as client:
10
+ response = client.get("/users")
11
+ assert response.status_code == 200
12
+ data = response.json()
13
+ assert "users" in data
14
+ assert isinstance(data["users"], list)
15
+ ```
16
+
17
+ ## Async Client (httpx.AsyncClient)
18
+
19
+ ```python
20
+ import httpx
21
+ import pytest
22
+
23
+ @pytest.mark.asyncio
24
+ async def test_get_users_async():
25
+ async with httpx.AsyncClient(base_url="https://api.example.com") as client:
26
+ response = await client.get("/users")
27
+ assert response.status_code == 200
28
+ data = response.json()
29
+ assert "users" in data
30
+ ```
31
+
32
+ ## CRUD Endpoints
33
+
34
+ ### GET (List)
35
+
36
+ ```python
37
+ def test_get_users_list(client, base_url):
38
+ response = client.get(f"{base_url}/users")
39
+ assert response.status_code == 200
40
+ assert "application/json" in response.headers.get("content-type", "")
41
+ data = response.json()
42
+ assert isinstance(data.get("users"), list)
43
+
44
+ def test_get_users_unauthorized(client, base_url):
45
+ response = client.get(f"{base_url}/users") # No auth
46
+ assert response.status_code == 401
47
+ ```
48
+
49
+ ### GET (Single)
50
+
51
+ ```python
52
+ def test_get_user_by_id(client, base_url, auth_headers):
53
+ response = client.get(f"{base_url}/users/1", headers=auth_headers)
54
+ assert response.status_code == 200
55
+ user = response.json()
56
+ assert user["id"] == 1
57
+ assert "email" in user
58
+
59
+ def test_get_user_not_found(client, base_url, auth_headers):
60
+ response = client.get(f"{base_url}/users/99999", headers=auth_headers)
61
+ assert response.status_code == 404
62
+ ```
63
+
64
+ ### POST (Create)
65
+
66
+ ```python
67
+ def test_create_user(client, base_url, auth_headers):
68
+ payload = {"email": "test@example.com", "name": "Test User"}
69
+ response = client.post(f"{base_url}/users", json=payload, headers=auth_headers)
70
+ assert response.status_code == 201
71
+ user = response.json()
72
+ assert "id" in user
73
+ assert user["email"] == "test@example.com"
74
+
75
+ def test_create_user_validation_error(client, base_url, auth_headers):
76
+ response = client.post(
77
+ f"{base_url}/users",
78
+ json={"email": "invalid"},
79
+ headers=auth_headers
80
+ )
81
+ assert response.status_code == 400
82
+ errors = response.json()
83
+ assert "errors" in errors or "message" in errors
84
+ ```
85
+
86
+ ### PUT/PATCH (Update)
87
+
88
+ ```python
89
+ def test_update_user(client, base_url, auth_headers):
90
+ response = client.put(
91
+ f"{base_url}/users/1",
92
+ json={"name": "Updated Name"},
93
+ headers=auth_headers
94
+ )
95
+ assert response.status_code == 200
96
+ user = response.json()
97
+ assert user["name"] == "Updated Name"
98
+ ```
99
+
100
+ ### DELETE
101
+
102
+ ```python
103
+ def test_delete_user(client, base_url, auth_headers):
104
+ response = client.delete(f"{base_url}/users/1", headers=auth_headers)
105
+ assert response.status_code == 204
106
+ assert response.content == b""
107
+
108
+ def test_delete_user_not_found(client, base_url, auth_headers):
109
+ response = client.delete(f"{base_url}/users/99999", headers=auth_headers)
110
+ assert response.status_code == 404
111
+ ```
112
+
113
+ ## Authentication
114
+
115
+ ### Bearer Token
116
+
117
+ ```python
118
+ @pytest.fixture
119
+ def auth_headers(token):
120
+ return {"Authorization": f"Bearer {token}"}
121
+
122
+ @pytest.fixture
123
+ def token(client, base_url):
124
+ response = client.post(
125
+ f"{base_url}/auth/login",
126
+ json={"email": "test@example.com", "password": "secret"}
127
+ )
128
+ assert response.status_code == 200
129
+ return response.json()["token"]
130
+
131
+ def test_protected_route(client, base_url, auth_headers):
132
+ response = client.get(f"{base_url}/users", headers=auth_headers)
133
+ assert response.status_code == 200
134
+ ```
135
+
136
+ ### API Key
137
+
138
+ ```python
139
+ @pytest.fixture
140
+ def api_key_headers():
141
+ return {"X-API-Key": os.environ.get("TEST_API_KEY", "test-key")}
142
+
143
+ def test_with_api_key(client, base_url, api_key_headers):
144
+ response = client.get(f"{base_url}/api/data", headers=api_key_headers)
145
+ assert response.status_code == 200
146
+ ```
147
+
148
+ ### Basic Auth
149
+
150
+ ```python
151
+ def test_basic_auth(client, base_url):
152
+ auth = httpx.BasicAuth("user", "password")
153
+ response = client.get(f"{base_url}/admin", auth=auth)
154
+ assert response.status_code == 200
155
+ ```
156
+
157
+ ### OAuth2
158
+
159
+ ```python
160
+ # Manual token flow
161
+ def test_oauth2_protected(client, base_url, oauth_token):
162
+ response = client.get(
163
+ f"{base_url}/protected",
164
+ headers={"Authorization": f"Bearer {oauth_token}"}
165
+ )
166
+ assert response.status_code == 200
167
+ ```
168
+
169
+ ### Cookies (Session)
170
+
171
+ ```python
172
+ def test_session_cookies(client, base_url):
173
+ # Login to set cookies
174
+ client.post(
175
+ f"{base_url}/auth/login",
176
+ json={"email": "test@example.com", "password": "secret"}
177
+ )
178
+ # Subsequent request uses session cookies
179
+ response = client.get(f"{base_url}/users")
180
+ assert response.status_code == 200
181
+ ```
182
+
183
+ ## File Upload
184
+
185
+ ```python
186
+ def test_file_upload(client, base_url, auth_headers, tmp_path):
187
+ file_path = tmp_path / "sample.pdf"
188
+ file_path.write_bytes(b"%PDF-1.4 fake content")
189
+
190
+ with open(file_path, "rb") as f:
191
+ files = {"file": ("sample.pdf", f, "application/pdf")}
192
+ response = client.post(
193
+ f"{base_url}/upload",
194
+ files=files,
195
+ headers=auth_headers
196
+ )
197
+
198
+ assert response.status_code == 201
199
+ data = response.json()
200
+ assert "url" in data or "id" in data
201
+ ```
202
+
203
+ ## Streaming
204
+
205
+ ```python
206
+ def test_streaming_response(client, base_url):
207
+ with client.stream("GET", f"{base_url}/stream") as response:
208
+ assert response.status_code == 200
209
+ chunks = []
210
+ for chunk in response.iter_bytes():
211
+ chunks.append(chunk)
212
+ assert len(chunks) > 0
213
+ ```
214
+
215
+ ## Query Parameters
216
+
217
+ ```python
218
+ def test_query_params(client, base_url):
219
+ response = client.get(
220
+ f"{base_url}/search",
221
+ params={"q": "test", "limit": 5}
222
+ )
223
+ assert response.status_code == 200
224
+ # URL: /search?q=test&limit=5
225
+ ```
226
+
227
+ ## Retry Logic
228
+
229
+ ```python
230
+ from httpx import Limits, Timeout
231
+
232
+ @pytest.fixture
233
+ def client_with_retry(base_url):
234
+ transport = httpx.HTTPTransport(retries=3)
235
+ with httpx.Client(
236
+ base_url=base_url,
237
+ transport=transport,
238
+ timeout=Timeout(30.0)
239
+ ) as client:
240
+ yield client
241
+ ```
242
+
243
+ ## requests Fallback
244
+
245
+ When httpx is not available, use requests with similar patterns:
246
+
247
+ ```python
248
+ import requests
249
+
250
+ def test_with_requests(base_url):
251
+ response = requests.get(f"{base_url}/users")
252
+ assert response.status_code == 200
253
+ data = response.json()
254
+ assert "users" in data
255
+
256
+ # Session for connection reuse
257
+ def test_with_session(base_url):
258
+ with requests.Session() as session:
259
+ session.headers.update({"Authorization": f"Bearer {token}"})
260
+ response = session.get(f"{base_url}/users")
261
+ assert response.status_code == 200
262
+ ```