pytest-api-cov 1.0.0__tar.gz → 1.0.2__tar.gz
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.
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/PKG-INFO +149 -30
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/README.md +139 -20
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/pyproject.toml +16 -29
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/src/pytest_api_cov/__init__.py +1 -0
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/src/pytest_api_cov/cli.py +30 -13
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/src/pytest_api_cov/config.py +2 -0
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/src/pytest_api_cov/plugin.py +374 -270
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/src/pytest_api_cov/pytest_flags.py +8 -0
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/.gitignore +0 -0
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/LICENSE +0 -0
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/src/pytest_api_cov/frameworks.py +0 -0
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/src/pytest_api_cov/models.py +0 -0
- {pytest_api_cov-1.0.0 → pytest_api_cov-1.0.2}/src/pytest_api_cov/report.py +0 -0
@@ -1,19 +1,19 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pytest-api-cov
|
3
|
-
Version: 1.0.
|
4
|
-
Summary:
|
3
|
+
Version: 1.0.2
|
4
|
+
Summary: Pytest Plugin to provide API Coverage statistics for Python Web Frameworks
|
5
5
|
Author-email: Barnaby Gill <barnabasgill@gmail.com>
|
6
6
|
License: Apache-2.0
|
7
7
|
License-File: LICENSE
|
8
8
|
Requires-Python: >=3.10
|
9
|
-
Requires-Dist: fastapi>=0.
|
10
|
-
Requires-Dist: flask>=
|
11
|
-
Requires-Dist: httpx>=0.
|
12
|
-
Requires-Dist: pydantic>=2.
|
13
|
-
Requires-Dist: pytest>=
|
14
|
-
Requires-Dist: rich>=
|
15
|
-
Requires-Dist: starlette>=0.
|
16
|
-
Requires-Dist: tomli>=
|
9
|
+
Requires-Dist: fastapi>=0.68.0
|
10
|
+
Requires-Dist: flask>=2.0.0
|
11
|
+
Requires-Dist: httpx>=0.20.0
|
12
|
+
Requires-Dist: pydantic>=2.0.0
|
13
|
+
Requires-Dist: pytest>=6.0.0
|
14
|
+
Requires-Dist: rich>=10.0.0
|
15
|
+
Requires-Dist: starlette>=0.14.0
|
16
|
+
Requires-Dist: tomli>=1.2.0
|
17
17
|
Description-Content-Type: text/markdown
|
18
18
|
|
19
19
|
# pytest-api-cov
|
@@ -44,11 +44,28 @@ For most projects, no configuration is needed:
|
|
44
44
|
pytest --api-cov-report
|
45
45
|
```
|
46
46
|
|
47
|
+
### App Location Flexibility
|
48
|
+
|
49
|
+
**Zero Config**: Works automatically if your app is in `app.py`, `main.py`, or `server.py`
|
50
|
+
|
51
|
+
**Any Location**: Place your app anywhere in your project - just create a `conftest.py`:
|
52
|
+
|
53
|
+
```python
|
54
|
+
import pytest
|
55
|
+
from my_project.backend.api import my_app # Any import path!
|
56
|
+
|
57
|
+
@pytest.fixture
|
58
|
+
def app():
|
59
|
+
return my_app
|
60
|
+
```
|
61
|
+
|
47
62
|
The plugin will automatically discover your Flask/FastAPI app if it's in common locations:
|
48
63
|
- `app.py` (with variable `app`, `application`, or `main`)
|
49
64
|
- `main.py` (with variable `app`, `application`, or `main`)
|
50
65
|
- `server.py` (with variable `app`, `application`, or `server`)
|
51
66
|
|
67
|
+
**Your app can be located anywhere!** If it's not in a standard location, just create a `conftest.py` file to tell the plugin where to find it.
|
68
|
+
|
52
69
|
### Example
|
53
70
|
|
54
71
|
Given this FastAPI app in `app.py`:
|
@@ -74,12 +91,12 @@ def health_check():
|
|
74
91
|
And this test file:
|
75
92
|
|
76
93
|
```python
|
77
|
-
def test_root_endpoint(
|
78
|
-
response =
|
94
|
+
def test_root_endpoint(coverage_client):
|
95
|
+
response = coverage_client.get("/")
|
79
96
|
assert response.status_code == 200
|
80
97
|
|
81
|
-
def test_get_user(
|
82
|
-
response =
|
98
|
+
def test_get_user(coverage_client):
|
99
|
+
response = coverage_client.get("/users/123")
|
83
100
|
assert response.status_code == 200
|
84
101
|
```
|
85
102
|
|
@@ -93,6 +110,22 @@ Uncovered Endpoints:
|
|
93
110
|
Total API Coverage: 66.67%
|
94
111
|
```
|
95
112
|
|
113
|
+
Or running with advanced options `pytest --api-cov-report --api-cov-show-covered-endpoints --api-cov-exclusion-patterns="/users/*" --api-cov-show-excluded-endpoints --api-cov-report-path=api_coverage.json --api-cov-fail-under=49` produces:
|
114
|
+
|
115
|
+
```
|
116
|
+
API Coverage Report
|
117
|
+
Uncovered Endpoints:
|
118
|
+
[X] /health
|
119
|
+
Covered Endpoints:
|
120
|
+
[.] /
|
121
|
+
Excluded Endpoints:
|
122
|
+
[-] /users/{user_id}
|
123
|
+
|
124
|
+
SUCCESS: Coverage of 50.0% meets requirement of 49.0%
|
125
|
+
|
126
|
+
JSON report saved to api_coverage.json
|
127
|
+
```
|
128
|
+
|
96
129
|
## Advanced Configuration
|
97
130
|
|
98
131
|
### Setup Wizard
|
@@ -110,15 +143,56 @@ This will:
|
|
110
143
|
|
111
144
|
### Manual Configuration
|
112
145
|
|
113
|
-
Create a `conftest.py` file:
|
146
|
+
Create a `conftest.py` file to specify your app location (works with **any** file path or structure):
|
114
147
|
|
115
148
|
```python
|
116
149
|
import pytest
|
117
|
-
|
150
|
+
|
151
|
+
# Import from anywhere in your project
|
152
|
+
from my_project.backend.api import flask_app
|
153
|
+
# or from src.services.web_server import fastapi_instance
|
154
|
+
# or from deeply.nested.modules import my_app
|
118
155
|
|
119
156
|
@pytest.fixture
|
120
157
|
def app():
|
121
|
-
return app
|
158
|
+
return flask_app # Return your app instance
|
159
|
+
```
|
160
|
+
|
161
|
+
This approach works with any project structure - the plugin doesn't care where your app is located as long as you can import it.
|
162
|
+
|
163
|
+
### Custom Test Client Fixtures
|
164
|
+
|
165
|
+
If you have an existing test client fixture with custom setup (authentication, headers, etc.), you can wrap it with coverage tracking:
|
166
|
+
|
167
|
+
```python
|
168
|
+
import pytest
|
169
|
+
from fastapi.testclient import TestClient
|
170
|
+
from your_app import app
|
171
|
+
|
172
|
+
@pytest.fixture
|
173
|
+
def my_custom_client():
|
174
|
+
"""Custom test client with authentication."""
|
175
|
+
client = TestClient(app)
|
176
|
+
client.headers.update({"Authorization": "Bearer test-token"})
|
177
|
+
return client
|
178
|
+
|
179
|
+
def test_endpoint(coverage_client):
|
180
|
+
# coverage_client will be your custom client with coverage tracking
|
181
|
+
response = coverage_client.get("/protected-endpoint")
|
182
|
+
assert response.status_code == 200
|
183
|
+
```
|
184
|
+
|
185
|
+
Configure it in `pyproject.toml`:
|
186
|
+
|
187
|
+
```toml
|
188
|
+
[tool.pytest_api_cov]
|
189
|
+
client_fixture_name = "my_custom_client"
|
190
|
+
```
|
191
|
+
|
192
|
+
Or via command line:
|
193
|
+
|
194
|
+
```bash
|
195
|
+
pytest --api-cov-report --api-cov-client-fixture-name=my_custom_client
|
122
196
|
```
|
123
197
|
|
124
198
|
### Configuration Options
|
@@ -153,6 +227,9 @@ force_sugar = true
|
|
153
227
|
|
154
228
|
# Force no Unicode symbols in output
|
155
229
|
force_sugar_disabled = true
|
230
|
+
|
231
|
+
# Wrap an existing custom test client fixture with coverage tracking
|
232
|
+
client_fixture_name = "my_custom_client"
|
156
233
|
```
|
157
234
|
|
158
235
|
### Command Line Options
|
@@ -194,7 +271,6 @@ Works automatically with FastAPI and Flask applications.
|
|
194
271
|
|
195
272
|
```python
|
196
273
|
from fastapi import FastAPI
|
197
|
-
from fastapi.testclient import TestClient
|
198
274
|
|
199
275
|
app = FastAPI()
|
200
276
|
|
@@ -202,9 +278,9 @@ app = FastAPI()
|
|
202
278
|
def read_item(item_id: int):
|
203
279
|
return {"item_id": item_id}
|
204
280
|
|
205
|
-
# Tests automatically get a '
|
206
|
-
def test_read_item(
|
207
|
-
response =
|
281
|
+
# Tests automatically get a 'coverage_client' fixture
|
282
|
+
def test_read_item(coverage_client):
|
283
|
+
response = coverage_client.get("/items/42")
|
208
284
|
assert response.status_code == 200
|
209
285
|
```
|
210
286
|
|
@@ -219,9 +295,9 @@ app = Flask(__name__)
|
|
219
295
|
def get_user(user_id):
|
220
296
|
return {"user_id": user_id}
|
221
297
|
|
222
|
-
# Tests automatically get a '
|
223
|
-
def test_get_user(
|
224
|
-
response =
|
298
|
+
# Tests automatically get a 'coverage_client' fixture
|
299
|
+
def test_get_user(coverage_client):
|
300
|
+
response = coverage_client.get("/users/123")
|
225
301
|
assert response.status_code == 200
|
226
302
|
```
|
227
303
|
|
@@ -301,13 +377,56 @@ jobs:
|
|
301
377
|
|
302
378
|
### No App Found
|
303
379
|
|
304
|
-
If you see "No API app found",
|
380
|
+
If you see "No API app found", you have several options:
|
381
|
+
|
382
|
+
**Option 1 - Auto-discovery (Zero Config)**
|
383
|
+
Place your app in a standard location with a standard name:
|
384
|
+
- Files: `app.py`, `main.py`, `server.py`, `wsgi.py`, `asgi.py`
|
385
|
+
- Variable names: `app`, `application`, `main`, `server`
|
386
|
+
|
387
|
+
**Option 2 - Custom Location (Any File/Path)**
|
388
|
+
Create a `conftest.py` file to specify your app location:
|
389
|
+
|
390
|
+
```python
|
391
|
+
import pytest
|
392
|
+
from my_project.api.server import my_flask_app # Any import path
|
393
|
+
# or from src.backend.main import fastapi_instance
|
394
|
+
# or from anywhere import your_app
|
395
|
+
|
396
|
+
@pytest.fixture
|
397
|
+
def app():
|
398
|
+
return my_flask_app # Return your app instance
|
399
|
+
```
|
400
|
+
|
401
|
+
**Option 3 - Override Auto-discovery**
|
402
|
+
If you have multiple auto-discoverable files or want to use a different app:
|
403
|
+
|
404
|
+
```python
|
405
|
+
# Even if you have app.py, you can override it
|
406
|
+
import pytest
|
407
|
+
from main import my_real_app # Use this instead of app.py
|
408
|
+
|
409
|
+
@pytest.fixture
|
410
|
+
def app():
|
411
|
+
return my_real_app
|
412
|
+
```
|
413
|
+
|
414
|
+
**Option 4 - Setup Wizard**
|
415
|
+
Run the interactive setup: `pytest-api-cov init`
|
416
|
+
|
417
|
+
The plugin will automatically find your app using the `app` fixture first, then fall back to auto-discovery in common locations. This means you can place your app **anywhere** as long as you create the fixture.
|
418
|
+
|
419
|
+
### Multiple App Files
|
420
|
+
|
421
|
+
If you have multiple files that could be auto-discovered (e.g., both `app.py` and `main.py`), the plugin will use the **first valid app it finds** in this priority order:
|
305
422
|
|
306
|
-
1.
|
307
|
-
2.
|
308
|
-
3.
|
423
|
+
1. `app.py`
|
424
|
+
2. `main.py`
|
425
|
+
3. `server.py`
|
426
|
+
4. `wsgi.py`
|
427
|
+
5. `asgi.py`
|
309
428
|
|
310
|
-
|
429
|
+
To use a specific app when multiple exist, create a `conftest.py` with an `app` fixture pointing to your preferred app.
|
311
430
|
|
312
431
|
### No Endpoints Discovered
|
313
432
|
|
@@ -315,7 +434,7 @@ If you see "No endpoints discovered":
|
|
315
434
|
|
316
435
|
1. Check that your app is properly instantiated
|
317
436
|
2. Verify your routes/endpoints are defined
|
318
|
-
3. Ensure the `
|
437
|
+
3. Ensure the `coverage_client` fixture is working in your tests
|
319
438
|
4. Use `-v` or `-vv` for debug information
|
320
439
|
|
321
440
|
### Framework Not Detected
|
@@ -26,11 +26,28 @@ For most projects, no configuration is needed:
|
|
26
26
|
pytest --api-cov-report
|
27
27
|
```
|
28
28
|
|
29
|
+
### App Location Flexibility
|
30
|
+
|
31
|
+
**Zero Config**: Works automatically if your app is in `app.py`, `main.py`, or `server.py`
|
32
|
+
|
33
|
+
**Any Location**: Place your app anywhere in your project - just create a `conftest.py`:
|
34
|
+
|
35
|
+
```python
|
36
|
+
import pytest
|
37
|
+
from my_project.backend.api import my_app # Any import path!
|
38
|
+
|
39
|
+
@pytest.fixture
|
40
|
+
def app():
|
41
|
+
return my_app
|
42
|
+
```
|
43
|
+
|
29
44
|
The plugin will automatically discover your Flask/FastAPI app if it's in common locations:
|
30
45
|
- `app.py` (with variable `app`, `application`, or `main`)
|
31
46
|
- `main.py` (with variable `app`, `application`, or `main`)
|
32
47
|
- `server.py` (with variable `app`, `application`, or `server`)
|
33
48
|
|
49
|
+
**Your app can be located anywhere!** If it's not in a standard location, just create a `conftest.py` file to tell the plugin where to find it.
|
50
|
+
|
34
51
|
### Example
|
35
52
|
|
36
53
|
Given this FastAPI app in `app.py`:
|
@@ -56,12 +73,12 @@ def health_check():
|
|
56
73
|
And this test file:
|
57
74
|
|
58
75
|
```python
|
59
|
-
def test_root_endpoint(
|
60
|
-
response =
|
76
|
+
def test_root_endpoint(coverage_client):
|
77
|
+
response = coverage_client.get("/")
|
61
78
|
assert response.status_code == 200
|
62
79
|
|
63
|
-
def test_get_user(
|
64
|
-
response =
|
80
|
+
def test_get_user(coverage_client):
|
81
|
+
response = coverage_client.get("/users/123")
|
65
82
|
assert response.status_code == 200
|
66
83
|
```
|
67
84
|
|
@@ -75,6 +92,22 @@ Uncovered Endpoints:
|
|
75
92
|
Total API Coverage: 66.67%
|
76
93
|
```
|
77
94
|
|
95
|
+
Or running with advanced options `pytest --api-cov-report --api-cov-show-covered-endpoints --api-cov-exclusion-patterns="/users/*" --api-cov-show-excluded-endpoints --api-cov-report-path=api_coverage.json --api-cov-fail-under=49` produces:
|
96
|
+
|
97
|
+
```
|
98
|
+
API Coverage Report
|
99
|
+
Uncovered Endpoints:
|
100
|
+
[X] /health
|
101
|
+
Covered Endpoints:
|
102
|
+
[.] /
|
103
|
+
Excluded Endpoints:
|
104
|
+
[-] /users/{user_id}
|
105
|
+
|
106
|
+
SUCCESS: Coverage of 50.0% meets requirement of 49.0%
|
107
|
+
|
108
|
+
JSON report saved to api_coverage.json
|
109
|
+
```
|
110
|
+
|
78
111
|
## Advanced Configuration
|
79
112
|
|
80
113
|
### Setup Wizard
|
@@ -92,15 +125,56 @@ This will:
|
|
92
125
|
|
93
126
|
### Manual Configuration
|
94
127
|
|
95
|
-
Create a `conftest.py` file:
|
128
|
+
Create a `conftest.py` file to specify your app location (works with **any** file path or structure):
|
96
129
|
|
97
130
|
```python
|
98
131
|
import pytest
|
99
|
-
|
132
|
+
|
133
|
+
# Import from anywhere in your project
|
134
|
+
from my_project.backend.api import flask_app
|
135
|
+
# or from src.services.web_server import fastapi_instance
|
136
|
+
# or from deeply.nested.modules import my_app
|
100
137
|
|
101
138
|
@pytest.fixture
|
102
139
|
def app():
|
103
|
-
return app
|
140
|
+
return flask_app # Return your app instance
|
141
|
+
```
|
142
|
+
|
143
|
+
This approach works with any project structure - the plugin doesn't care where your app is located as long as you can import it.
|
144
|
+
|
145
|
+
### Custom Test Client Fixtures
|
146
|
+
|
147
|
+
If you have an existing test client fixture with custom setup (authentication, headers, etc.), you can wrap it with coverage tracking:
|
148
|
+
|
149
|
+
```python
|
150
|
+
import pytest
|
151
|
+
from fastapi.testclient import TestClient
|
152
|
+
from your_app import app
|
153
|
+
|
154
|
+
@pytest.fixture
|
155
|
+
def my_custom_client():
|
156
|
+
"""Custom test client with authentication."""
|
157
|
+
client = TestClient(app)
|
158
|
+
client.headers.update({"Authorization": "Bearer test-token"})
|
159
|
+
return client
|
160
|
+
|
161
|
+
def test_endpoint(coverage_client):
|
162
|
+
# coverage_client will be your custom client with coverage tracking
|
163
|
+
response = coverage_client.get("/protected-endpoint")
|
164
|
+
assert response.status_code == 200
|
165
|
+
```
|
166
|
+
|
167
|
+
Configure it in `pyproject.toml`:
|
168
|
+
|
169
|
+
```toml
|
170
|
+
[tool.pytest_api_cov]
|
171
|
+
client_fixture_name = "my_custom_client"
|
172
|
+
```
|
173
|
+
|
174
|
+
Or via command line:
|
175
|
+
|
176
|
+
```bash
|
177
|
+
pytest --api-cov-report --api-cov-client-fixture-name=my_custom_client
|
104
178
|
```
|
105
179
|
|
106
180
|
### Configuration Options
|
@@ -135,6 +209,9 @@ force_sugar = true
|
|
135
209
|
|
136
210
|
# Force no Unicode symbols in output
|
137
211
|
force_sugar_disabled = true
|
212
|
+
|
213
|
+
# Wrap an existing custom test client fixture with coverage tracking
|
214
|
+
client_fixture_name = "my_custom_client"
|
138
215
|
```
|
139
216
|
|
140
217
|
### Command Line Options
|
@@ -176,7 +253,6 @@ Works automatically with FastAPI and Flask applications.
|
|
176
253
|
|
177
254
|
```python
|
178
255
|
from fastapi import FastAPI
|
179
|
-
from fastapi.testclient import TestClient
|
180
256
|
|
181
257
|
app = FastAPI()
|
182
258
|
|
@@ -184,9 +260,9 @@ app = FastAPI()
|
|
184
260
|
def read_item(item_id: int):
|
185
261
|
return {"item_id": item_id}
|
186
262
|
|
187
|
-
# Tests automatically get a '
|
188
|
-
def test_read_item(
|
189
|
-
response =
|
263
|
+
# Tests automatically get a 'coverage_client' fixture
|
264
|
+
def test_read_item(coverage_client):
|
265
|
+
response = coverage_client.get("/items/42")
|
190
266
|
assert response.status_code == 200
|
191
267
|
```
|
192
268
|
|
@@ -201,9 +277,9 @@ app = Flask(__name__)
|
|
201
277
|
def get_user(user_id):
|
202
278
|
return {"user_id": user_id}
|
203
279
|
|
204
|
-
# Tests automatically get a '
|
205
|
-
def test_get_user(
|
206
|
-
response =
|
280
|
+
# Tests automatically get a 'coverage_client' fixture
|
281
|
+
def test_get_user(coverage_client):
|
282
|
+
response = coverage_client.get("/users/123")
|
207
283
|
assert response.status_code == 200
|
208
284
|
```
|
209
285
|
|
@@ -283,13 +359,56 @@ jobs:
|
|
283
359
|
|
284
360
|
### No App Found
|
285
361
|
|
286
|
-
If you see "No API app found",
|
362
|
+
If you see "No API app found", you have several options:
|
363
|
+
|
364
|
+
**Option 1 - Auto-discovery (Zero Config)**
|
365
|
+
Place your app in a standard location with a standard name:
|
366
|
+
- Files: `app.py`, `main.py`, `server.py`, `wsgi.py`, `asgi.py`
|
367
|
+
- Variable names: `app`, `application`, `main`, `server`
|
368
|
+
|
369
|
+
**Option 2 - Custom Location (Any File/Path)**
|
370
|
+
Create a `conftest.py` file to specify your app location:
|
371
|
+
|
372
|
+
```python
|
373
|
+
import pytest
|
374
|
+
from my_project.api.server import my_flask_app # Any import path
|
375
|
+
# or from src.backend.main import fastapi_instance
|
376
|
+
# or from anywhere import your_app
|
377
|
+
|
378
|
+
@pytest.fixture
|
379
|
+
def app():
|
380
|
+
return my_flask_app # Return your app instance
|
381
|
+
```
|
382
|
+
|
383
|
+
**Option 3 - Override Auto-discovery**
|
384
|
+
If you have multiple auto-discoverable files or want to use a different app:
|
385
|
+
|
386
|
+
```python
|
387
|
+
# Even if you have app.py, you can override it
|
388
|
+
import pytest
|
389
|
+
from main import my_real_app # Use this instead of app.py
|
390
|
+
|
391
|
+
@pytest.fixture
|
392
|
+
def app():
|
393
|
+
return my_real_app
|
394
|
+
```
|
395
|
+
|
396
|
+
**Option 4 - Setup Wizard**
|
397
|
+
Run the interactive setup: `pytest-api-cov init`
|
398
|
+
|
399
|
+
The plugin will automatically find your app using the `app` fixture first, then fall back to auto-discovery in common locations. This means you can place your app **anywhere** as long as you create the fixture.
|
400
|
+
|
401
|
+
### Multiple App Files
|
402
|
+
|
403
|
+
If you have multiple files that could be auto-discovered (e.g., both `app.py` and `main.py`), the plugin will use the **first valid app it finds** in this priority order:
|
287
404
|
|
288
|
-
1.
|
289
|
-
2.
|
290
|
-
3.
|
405
|
+
1. `app.py`
|
406
|
+
2. `main.py`
|
407
|
+
3. `server.py`
|
408
|
+
4. `wsgi.py`
|
409
|
+
5. `asgi.py`
|
291
410
|
|
292
|
-
|
411
|
+
To use a specific app when multiple exist, create a `conftest.py` with an `app` fixture pointing to your preferred app.
|
293
412
|
|
294
413
|
### No Endpoints Discovered
|
295
414
|
|
@@ -297,7 +416,7 @@ If you see "No endpoints discovered":
|
|
297
416
|
|
298
417
|
1. Check that your app is properly instantiated
|
299
418
|
2. Verify your routes/endpoints are defined
|
300
|
-
3. Ensure the `
|
419
|
+
3. Ensure the `coverage_client` fixture is working in your tests
|
301
420
|
4. Use `-v` or `-vv` for debug information
|
302
421
|
|
303
422
|
### Framework Not Detected
|
@@ -1,23 +1,21 @@
|
|
1
1
|
[project]
|
2
2
|
name = "pytest-api-cov"
|
3
|
-
version = "1.0.
|
4
|
-
description = "
|
3
|
+
version = "1.0.2"
|
4
|
+
description = "Pytest Plugin to provide API Coverage statistics for Python Web Frameworks"
|
5
5
|
readme = "README.md"
|
6
|
-
authors = [
|
7
|
-
|
8
|
-
]
|
9
|
-
license = {text = "Apache-2.0"}
|
6
|
+
authors = [{ name = "Barnaby Gill", email = "barnabasgill@gmail.com" }]
|
7
|
+
license = { text = "Apache-2.0" }
|
10
8
|
requires-python = ">=3.10"
|
11
9
|
|
12
10
|
dependencies = [
|
13
|
-
"fastapi>=0.
|
14
|
-
"flask>=
|
15
|
-
"httpx>=0.
|
16
|
-
"pydantic>=2.
|
17
|
-
"rich>=
|
18
|
-
"starlette>=0.
|
19
|
-
"tomli>=
|
20
|
-
"pytest>=
|
11
|
+
"fastapi>=0.68.0",
|
12
|
+
"flask>=2.0.0",
|
13
|
+
"httpx>=0.20.0",
|
14
|
+
"pydantic>=2.0.0",
|
15
|
+
"rich>=10.0.0",
|
16
|
+
"starlette>=0.14.0",
|
17
|
+
"tomli>=1.2.0",
|
18
|
+
"pytest>=6.0.0",
|
21
19
|
]
|
22
20
|
|
23
21
|
[dependency-groups]
|
@@ -32,13 +30,11 @@ dev = [
|
|
32
30
|
|
33
31
|
# API COVERAGE
|
34
32
|
[tool.pytest_api_cov]
|
35
|
-
|
33
|
+
fail_under = 70
|
36
34
|
show_covered_endpoints = true
|
37
35
|
show_uncovered_endpoints = true
|
38
36
|
show_excluded_endpoints = true
|
39
|
-
exclusion_patterns = [
|
40
|
-
"xyz",
|
41
|
-
]
|
37
|
+
exclusion_patterns = ["xyz"]
|
42
38
|
report_path = "reports/pytest_api_cov.json"
|
43
39
|
|
44
40
|
[tool.pytest.ini_options]
|
@@ -46,12 +42,8 @@ testpaths = ["tests/unit", "tests/integration", "example/tests"]
|
|
46
42
|
pythonpath = ["src"]
|
47
43
|
|
48
44
|
[tool.coverage.report]
|
49
|
-
exclude_lines = [
|
50
|
-
'if __name__ == "__main__":',
|
51
|
-
"if TYPE_CHECKING:",
|
52
|
-
]
|
45
|
+
exclude_lines = ['if __name__ == "__main__":', "if TYPE_CHECKING:"]
|
53
46
|
show_missing = true
|
54
|
-
fail_under = 70.0
|
55
47
|
|
56
48
|
|
57
49
|
[build-system]
|
@@ -62,12 +54,7 @@ build-backend = "hatchling.build"
|
|
62
54
|
packages = ["src/pytest_api_cov"]
|
63
55
|
|
64
56
|
[tool.hatch.build.targets.sdist]
|
65
|
-
include = [
|
66
|
-
"src/pytest_api_cov/",
|
67
|
-
"README.md",
|
68
|
-
"LICENSE",
|
69
|
-
"pyproject.toml",
|
70
|
-
]
|
57
|
+
include = ["src/pytest_api_cov/", "README.md", "LICENSE", "pyproject.toml"]
|
71
58
|
|
72
59
|
# [[tool.uv.index]]
|
73
60
|
# name = "testpypi"
|
@@ -56,22 +56,36 @@ def generate_conftest_content(framework: str, file_path: str, app_variable: str)
|
|
56
56
|
|
57
57
|
import pytest
|
58
58
|
|
59
|
-
# Import your {framework} app
|
59
|
+
# Import your {framework} app from anywhere in your project
|
60
60
|
from {module_path} import {app_variable}
|
61
61
|
|
62
62
|
|
63
63
|
@pytest.fixture
|
64
|
-
def
|
65
|
-
"""Provide the {framework}
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def test_root_endpoint(client):
|
70
|
-
response = client.get("/")
|
71
|
-
assert response.status_code == 200
|
72
|
-
```
|
64
|
+
def app():
|
65
|
+
"""Provide the {framework} app for API coverage testing.
|
66
|
+
|
67
|
+
You can import from any location - just change the import path above
|
68
|
+
to match your project structure.
|
73
69
|
"""
|
74
70
|
return {app_variable}
|
71
|
+
|
72
|
+
|
73
|
+
# The plugin will automatically create a 'coverage_client' fixture that uses your 'app' fixture
|
74
|
+
# You can use either:
|
75
|
+
# - def test_endpoint(app): ... # Direct app access
|
76
|
+
# - def test_endpoint(coverage_client): ... # Test client with API coverage tracking
|
77
|
+
#
|
78
|
+
# To wrap an existing custom fixture instead, specify the fixture name in pyproject.toml:
|
79
|
+
# [tool.pytest_api_cov]
|
80
|
+
# client_fixture_name = "my_custom_client"
|
81
|
+
#
|
82
|
+
# Example custom fixture:
|
83
|
+
# @pytest.fixture
|
84
|
+
# def my_custom_client(app):
|
85
|
+
# client = app.test_client() # Flask
|
86
|
+
# # or client = TestClient(app) # FastAPI
|
87
|
+
# # Add custom setup here (auth headers, etc.)
|
88
|
+
# return client
|
75
89
|
'''
|
76
90
|
|
77
91
|
|
@@ -100,6 +114,9 @@ show_excluded_endpoints = false
|
|
100
114
|
|
101
115
|
# Force Unicode symbols in terminal output (optional)
|
102
116
|
# force_sugar = true
|
117
|
+
|
118
|
+
# Wrap an existing custom test client fixture with coverage tracking (optional)
|
119
|
+
# client_fixture_name = "my_custom_client"
|
103
120
|
"""
|
104
121
|
|
105
122
|
|
@@ -152,13 +169,13 @@ testpaths = ["tests"]
|
|
152
169
|
print("🎉 Setup complete!")
|
153
170
|
print()
|
154
171
|
print("Next steps:")
|
155
|
-
print("1. Write your tests using the '
|
172
|
+
print("1. Write your tests using the 'coverage_client' fixture")
|
156
173
|
print("2. Run: pytest --api-cov-report")
|
157
174
|
print()
|
158
175
|
print("Example test:")
|
159
176
|
print("""
|
160
|
-
def test_root_endpoint(
|
161
|
-
response =
|
177
|
+
def test_root_endpoint(coverage_client):
|
178
|
+
response = coverage_client.get("/")
|
162
179
|
assert response.status_code == 200
|
163
180
|
""")
|
164
181
|
|
@@ -20,6 +20,7 @@ class ApiCoverageReportConfig(BaseModel):
|
|
20
20
|
report_path: Optional[str] = Field(None, alias="api-cov-report-path")
|
21
21
|
force_sugar: bool = Field(False, alias="api-cov-force-sugar")
|
22
22
|
force_sugar_disabled: bool = Field(False, alias="api-cov-force-sugar-disabled")
|
23
|
+
client_fixture_name: str = Field("coverage_client", alias="api-cov-client-fixture-name")
|
23
24
|
|
24
25
|
|
25
26
|
def read_toml_config() -> Dict[str, Any]:
|
@@ -43,6 +44,7 @@ def read_session_config(session_config: Any) -> Dict[str, Any]:
|
|
43
44
|
"api-cov-report-path": "report_path",
|
44
45
|
"api-cov-force-sugar": "force_sugar",
|
45
46
|
"api-cov-force-sugar-disabled": "force_sugar_disabled",
|
47
|
+
"api-cov-client-fixture-name": "client_fixture_name",
|
46
48
|
}
|
47
49
|
config = {}
|
48
50
|
for opt, key in cli_options.items():
|