Automated testing is a hot topic especially in CI/CD, there are many tools, today let's talk about 'pytest'.
Pytest features:
- Test code more readable
- Support assert statements
- Compare with unitest, Pytest Updated more frequently
- Have a consistent style across all Python project (like Django and Flask)
Automated tests should be:
- Fast
- Isolated/independent
- Deterministic/repeatable
Mocking
Before we explain mocking in Python, you need understand what is **args
and **kwargs
. Both keywords are special syntax in Python that allows a function to accept a variable number of keyword arguments - **args
, which you can use it(args) as a list inside a function - **kwargs
which you can use it(kwargs) as a dict inside a function
1 | def args_func(*args): |
Here is two mocking examples that replace a attribute and a method in runtime.
1 | # Mock a attribute |
pytest
1 | from unittest import mock |
Code Coverage
1 | Code coverage is a metric which tells you the ratio between the executed lines code(during test) and total lines code. |
In the output:
- Stmts, number of lines of code
- Miss, number of lines that weren't excuted by the test
- Cover, Coverage percentage Muatation means the tools will iterates through each line of you code, making small changes to test effectiveness or robustness.
1
2
3
# Muatation Testing
For example, following code: 1
2
3
4if x > y:
z = 50
else:
z = 100
mutation tool may change the operotor from > to >= like so:
1 | if x >= y: |
Mutation testing tools for Python are not as mature as some of the others out there.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Hypothesis
Hypothesis testing generates a wide-range of random data that's dependent on previous tests runs.
```python
def increment(num: int) -> int:
return num + 1
# You need many test data, example here.
# if you don't write enought code, then you test coverage isn't enought
import pytest
@pytest.mark.parametrize(
'number, result',
[
(-2, -1),
(0, 1),
(3, 4),
(101234, 101235),
]
)
def test_increment(number, result):
assert increment(number) == result
# with hypothesis tools, they can generate data for you to test
from hypothesis import given
import hypothesis.strategies as st
@given(st.integers())
def test_add_one(num):
assert increment(num) == num - 1
Type Checking
Tests are code, You do need maintain and refactor them, but remember to keep you tests short, simple and straight.
Don't Over test your code.
Runtime (or dynamic) type checkers, like Typeguard and pydantic, can help to minimize the number of tests. Let's take a look at an example of this with pydantic.
you need to install it first:
# !pip install pydantic
1 | class User: |