You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(EN)Parametrizing fixtures and test functions — pytest documentation
245
+
246
+
While Python has the standard unittest library built-in for testing, the **pytest** library is widely used to write more flexible and readable tests. pytest comes with a simple API and powerful features, and can be easily installed with `pip install pytest`. You can run tests with the `$ pytest` command.
247
+
248
+
In Python, you can use the `pytest.mark.parametrize` decorator to describe multiple test cases together. Let's write a test for the say_hello function:
249
+
250
+
```python
251
+
# hello.py
252
+
defsay_hello(name=""):
253
+
if name:
254
+
returnf"Hello, {name}!"
255
+
return"Hello!"
256
+
257
+
# test_hello.py
258
+
import pytest
259
+
from hello import say_hello
260
+
261
+
@pytest.mark.parametrize("name, expected",[
262
+
("Alice", "Hello, Alice!"),
263
+
("", "Hello!"),
264
+
]
265
+
)
266
+
deftest_say_hello(name, expected):
267
+
got = say_hello(name)
268
+
269
+
# Check if the expected return value and the actual value are the same, and display an error if they differ
270
+
assert got == expected, f"unexpected result of say_hello: want={expected}, got={got}"
271
+
```
272
+
273
+
The need to consider argument design with testing in mind is common to both Python and Go. Let's consider modifying the `say_hello` implementation to change the greeting based on the time of day:
274
+
275
+
```python
276
+
from datetime import datetime
277
+
278
+
defsay_hello(name):
279
+
now = datetime.now() # Directly depends on the current time, making it difficult to test
280
+
current_hour = now.hour
281
+
282
+
if6<= current_hour <10:
283
+
returnf"Good morning, {name}!"
284
+
if10<= current_hour <18:
285
+
returnf"Hello, {name}!"
286
+
returnf"Good evening, {name}!"
287
+
```
288
+
289
+
This function is difficult to test because it directly depends on the current time. To test each time period, you would need to run the test at that specific time.
290
+
291
+
To make it more testable, we can rewrite the function as follows:
292
+
293
+
```python
294
+
# Improved code (more testable design)
295
+
from datetime import datetime
296
+
297
+
defsay_hello(name, now=None):
298
+
if now isNone:
299
+
now = datetime.now()
300
+
301
+
current_hour = now.hour
302
+
303
+
if6<= current_hour <10:
304
+
returnf"Good morning, {name}!"
305
+
if10<= current_hour <18:
306
+
returnf"Hello, {name}!"
307
+
returnf"Good evening, {name}!"
308
+
```
309
+
310
+
Now we can specify the current time as an argument. By setting None as the default value, we can still omit the now parameter in normal usage.
@@ -251,8 +342,11 @@ Let's write test cases for this.
251
342
- What does this test verify?
252
343
- What's the difference between `t.Error()` and `t.Fatal()`?
253
344
254
-
### Python
255
-
TBD
345
+
### Python (Read Only)
346
+
347
+
Python testing is implemented in [`main_test.py`](https://github.com/mercari-build/mercari-build-training/blob/main/python/main_test.py).
348
+
349
+
Unlike the Go API implementation, in Python API implementation using the FastAPI framework, developers do not need to implement HTTP Request parsing themselves. Therefore, no additional implementation is required in this chapter, but you should review the test code to deepen your understanding.
256
350
257
351
## 2. Writing Tests for the Hello Handler
258
352
@@ -283,7 +377,16 @@ Once you have the logic figured out, implement it.
In Python, we use FastAPI's `testclient.TestClient` to verify that the handler function `hello` works correctly. Let's edit the test function [test_hello](https://github.com/mercari-build/mercari-build-training/blob/main/python/main_test.py#L53) that's already provided and write a test.
383
+
384
+
As with Go, let's implement the test code with the following considerations in mind:
385
+
386
+
- What do you want to test with this handler?
387
+
- How can you verify that it behaves correctly?
388
+
389
+
For implementing the test, you may refer to the [official FastAPI documentation](https://fastapi.tiangolo.com/tutorial/testing/#testclient).
287
390
288
391
## 3. Writing Tests Using Mocks
289
392
@@ -311,9 +414,24 @@ Let's test both successful and failed persistence scenarios using mocks.
311
414
- Consider the benefits of using interfaces to satisfy mocks
For Python mock libraries, there are several options including the built-in standard `unittest.mock` and pytest's `pytest-mock`. Mocks become necessary when the process being tested depends on external tools or objects, such as in the following cases:
425
+
426
+
- Mocking database connections to test user authentication logic without connecting to an actual database.
427
+
- Mocking HTTP API clients to test weather forecast retrieval functions without actual network communication.
428
+
- Mocking the file system to test logging functionality without actual file operations.
429
+
430
+
In our case, we could consider implementing a test like the first example mentioned: "mocking database connections." However, the Build Python API implementation is very simple, and setting up classes like ItemRepository for mock testing would unnecessarily complicate the implementation.
431
+
432
+
Since sufficient verification can be done with the test code implemented in the chapter "4. Writing tests using actual databases," and because it would contradict Python's language philosophy of "simplicity" and "explicitness," **we have omitted Python implementations using mocks from this teaching material**.
433
+
434
+
However, in actual development environments where applications become more complex, there are many cases where tests using mocks are implemented in Python as well. If you're interested, take a look at the explanation of mock testing in the Go section, or review Python test implementations using mocks that are introduced on the internet.
317
435
318
436
## 4. Writing Tests Using Real Databases
319
437
@@ -332,7 +450,17 @@ After performing database operations, we need to verify the database state match
332
450
333
451
### Python
334
452
335
-
TBD
453
+
Let's write a test in Python using a test database (sqlite3). Uncomment the two places in [main_test.py](https://github.com/mercari-build/mercari-build-training/blob/main/python/main_test.py) that say "`STEP 6-4: uncomment this test setup`". ([first location](https://github.com/mercari-build/mercari-build-training/blob/main/python/main_test.py#L9-L42)/[second location](https://github.com/mercari-build/mercari-build-training/blob/main/python/main_test.py#L60-L84))
454
+
455
+
The `db_connection` function creates and sets up a new test database using sqlite3 before the test, and deletes the test database after the test is completed.
456
+
457
+
The `test_add_item_e2e` function tests the item addition functionality by sending a POST request to the API endpoint (`/items/`). This function runs with parameterized test cases (valid and invalid data). The test verifies:
458
+
459
+
1. Whether the response status code matches the expected value
460
+
2. For non-error cases, whether the response body contains a "message"
461
+
3. Whether the data was correctly saved in the database (matching name and category)
462
+
463
+
What's particularly important is that it tests end-to-end using an actual database (for testing) rather than mocks, which verifies the functionality in a way that's closer to the actual environment.
Pythonで、テスト用のデータベース(sqlite3)を用いたテストを書いていきましょう。[main_test.py]()の「`STEP 6-4: uncomment this test setup`」という記載がある二カ所をコメントアウトしてください。([一カ所目](https://github.com/mercari-build/mercari-build-training/blob/main/python/main_test.py#L9-L42)/[二カ所目](https://github.com/mercari-build/mercari-build-training/blob/main/python/main_test.py#L60-L84))
0 commit comments