It's easy if you use poetry.
poetry add -D pytest-mock
It's easy even if you're not using it. However, poetry is recommended.
python
pip install pytest-mock
```
# How to write a test
Specify `mocker` as an argument of the test method.
```python
class MyClassTests:
def test_something(self, mocker):
pass
```
# How to make a mock
There are two ways to create a [mock --Mock](https://docs.python.org/ja/3/library/unittest.mock.html#the-mock-class), one is to use `mocker.Mock` to create an instance, and the other is to use` mocker.patch` to dynamically replace it with a Mock instance.
Suppose you have a module like this as a subject.
```
exmock
├── __init__.py
├── bar.py
├── foo.py
└── target.py
```
#### **`exmock/target.py`**
```python
from .foo import FooClass
from .bar import BarClass
class TargetClass:
def do_something(self, foo: FooClass) -> None:
""" do_foo()Do the result of_bar()Pass to."""
bar = BarClass()
bar.do_bar(foo.do_foo())
```
## Make a class mock
```python
foo_mock = mocker.Mock(spec=FooClass)
# foo_mock.do_foo is also a mock.
```
By specifying the class as spec, only the attributes that actually exist in the class will grow.
The attributes that have grown are also instances of `Mock`.
## Mock the return value of the constructor
The constructor doesn't return a value. It's strange as an expression, but for convenience of explanation, it's done like this.
```python
bar_mock = mocker.Mock(spec=BarClass)
mocker.patch('exmock.target.BarClass', return_value=bar_mock)
```
The `exmock.target.BarClass` is patched to match the` from .bar import BarClass` in the test target file ** exmock/target.py **.
## Mock the method
```python
do_bar_mock = mocker.patch.object(BarClass, 'do_bar')
```
The implementation of the `do_bar` method is mocked and nothing happens.
To change the behavior of the `do_bar` method, use the return mock.
# Change the behavior of the mock
The generated mock can be moved arbitrarily according to the test.
## Set the return value
Set the value in the `return_value` attribute of the generated mock.
```python
mymock.return_value =Return value
```
## Set the return value of the property
It is OK if you set the value directly.
```python
mymock.foo =Return value
```
## Raise an exception when called
Set an exception to the `side_effect` attribute of the generated mock.
```python
mymock.side_effect = Exception('It is an error')
```
## Replace completely
It completely replaces the mocked implementation.
```python
def side_effect():
pass
mymock.side_effect = side_effect
```
# Mock test
Test that something has been done to the mock.
## Test that it was called once
```python
mymock.assert_called_once()
```
## Test that it was called multiple times
```python
#If you are using pytest.
assert mymock.call_count ==Number of times
```
## Test that it is not called at all
```python
mymock.assert_not_called()
```
## Test that it was called with a specific argument
```python
mymock.assert_called_with(Specific argument 1,Specific argument 2, ……)
mymock.assert_called_once_with(Specific argument 1,Specific argument 2, ……) #When called once
```
## Test complex arguments
```python
args, kwargs = mymock.call_args
#Test args and kwargs
```
`call_args` contains the most recent call argument.
`args` is a tuple that contains non-keyword arguments. `kwargs` is a dictionary that contains arguments with keywords.
# Verification environment
This is the environment I used to write this article.
* Python 3.8.2
* Poetry 1.1.4
* pytest-mock 3.4.0
I put the used files here.
https://github.com/sengokyu/python-pytest-mock-usage-example
Recommended Posts