|
75 | 75 | - test_user_story_1.py
|
76 | 76 | - test_user_story_2.py
|
77 | 77 | ```
|
| 78 | +### Adding `conftest.py` File |
78 | 79 |
|
| 80 | +The `conftest.py` file in a Python project is a centralized location for defining shared fixtures and configuration for your tests. It provides: |
| 81 | + |
| 82 | +- **Shared Resources**: Setting up shared resources like database connections, mock clients, or application instances. |
| 83 | +- **Code Reusability**: Reducing code duplication by providing reusable fixtures. |
| 84 | +- **Clarity**: Maintaining test clarity by isolating setup logic from test cases. |
| 85 | + |
| 86 | +##### Best Practices for `conftest.py` |
| 87 | + |
| 88 | +1. **Keep It Focused** |
| 89 | + - Only include shared configurations and fixtures. Avoid adding test cases or unrelated code. |
| 90 | + |
| 91 | +2. **Name Fixtures Clearly** |
| 92 | + - Use descriptive names to indicate their purpose (e.g., `temp_file`, `mock_service`). |
| 93 | + |
| 94 | +3. **Use Appropriate Scopes** |
| 95 | + - Choose the right fixture scope (`function`, `class`, `module`, `session`) based on how long the fixture should persist. |
| 96 | + |
| 97 | +4. **Minimize Side Effects** |
| 98 | + - Ensure fixtures clean up resources after execution to prevent interference between tests. |
| 99 | + |
| 100 | + |
| 101 | +--- |
| 102 | + |
| 103 | +### Code Coverage, Linting, and Formatting |
| 104 | + |
| 105 | +To ensure code quality, maintain consistency, and measure testing effectiveness, we can try including steps for **coverage**, **linting**, and **formatting** as part of the testing workflow. |
| 106 | + |
| 107 | +For example, we can add this in a `Makefile` or CI pipeline: |
| 108 | + |
| 109 | +```makefile |
| 110 | +test: lint test-coverage |
| 111 | + |
| 112 | +test-coverage:: coverage-unit coverage-integration |
| 113 | + |
| 114 | +coverage-unit: |
| 115 | + pytest --cov=src tests/unit/ |
| 116 | + |
| 117 | +coverage-integration: |
| 118 | + pytest --cov=src --cov-append --cov-fail-under=90 tests/integration/ |
| 119 | + |
| 120 | +lint:: black-check flake8 |
| 121 | + |
| 122 | +black-check: |
| 123 | + black --check . |
| 124 | + |
| 125 | +black: |
| 126 | + black . |
| 127 | + |
| 128 | +flake8: |
| 129 | + flake8 . |
| 130 | + |
| 131 | +``` |
79 | 132 |
|
80 | 133 | ## Testing Information By Language
|
81 | 134 |
|
@@ -134,3 +187,64 @@ class TestClassTwo:
|
134 | 187 | def test_process_test_something_else(self):
|
135 | 188 | .....
|
136 | 189 | ```
|
| 190 | + |
| 191 | + |
| 192 | +## Testing FastAPI Applications |
| 193 | + |
| 194 | +FastAPI provides built-in tools and conventions that simplify testing APIs. |
| 195 | +### `TestClient` |
| 196 | + |
| 197 | +FastAPI includes a `TestClient` utility (via Starlette) to simulate HTTP requests. |
| 198 | + |
| 199 | +```python |
| 200 | +from fastapi.testclient import TestClient |
| 201 | + |
| 202 | +# Create a test client for the FastAPI app |
| 203 | +client = TestClient(app) |
| 204 | + |
| 205 | +def test_endpoint(): |
| 206 | + response = client.get("/endpoint_api") |
| 207 | + assert response.status_code == 200 |
| 208 | + assert response.json() == {"key": "value"} # Replace with your expected response |
| 209 | +``` |
| 210 | + |
| 211 | + |
| 212 | +### `Testcontainers` |
| 213 | + |
| 214 | + |
| 215 | +**Testcontainers** is a library that helps you create lightweight, disposable containers for testing. It is particularly useful for testing services like databases without mocking, ensuring your tests run in a real-world environment. |
| 216 | + |
| 217 | +### Prerequisites |
| 218 | + |
| 219 | +* **Docker Desktop** (or any Docker runtime) |
| 220 | +* **Python >= 3.8** |
| 221 | + |
| 222 | +### Why Testcontainers? |
| 223 | + |
| 224 | +1. **Real Services**: Test against actual instances of databases, queues, etc. |
| 225 | +2. **Isolation**: Each test runs in its own container, avoiding conflicts. |
| 226 | +3. **Consistency**: Ensures the test environment matches production as closely as possible. |
| 227 | + |
| 228 | +### Example Usage with LocalStack Container. You can use Testcontainers to spin up a LocalStack container, which emulates AWS services |
| 229 | + |
| 230 | +```python |
| 231 | +from testcontainers.localstack import LocalStackContainer |
| 232 | +import boto3 |
| 233 | + |
| 234 | +def localstack_container(): |
| 235 | + # Start LocalStack container |
| 236 | + with LocalStackContainer() as localstack: |
| 237 | + yield localstack |
| 238 | + |
| 239 | +def s3_client(localstack_container): |
| 240 | + # Create an S3 client using the LocalStack endpoint |
| 241 | + s3 = boto3.client( |
| 242 | + "s3", |
| 243 | + endpoint_url=localstack_container.get_url(), |
| 244 | + region_name=os.environ["AWS_DEFAULT_REGION"], |
| 245 | + aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"], |
| 246 | + aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"], |
| 247 | + ) |
| 248 | + return s3 |
| 249 | + |
| 250 | + |
0 commit comments