When I wrote the test code using pytest, I had a hard time using Mock, so I summarized it.
Mock is a module that facilitates unit testing on behalf of specific objects.
Install pytest
pip install pytest pytest-mock
Create code to test
Directory structure
```bash
root
├─src
│   └─script.py ・ ・ ・ Source code to be tested
└─tests
    ├─__init__.py ・ ・ ・ empty file
    └─script.py ・ ・ ・ Test code
```
Source code to be tested
```python:src/script.py
import requests
def sample1(url):
    return sample2(url)
def sample2(url):
    try:
        response = requests.get(url)
        return response.status_code
    except Exception:
        return 0
class SampleClass:
    def __init__(self):
        self._req = requests
    def get(self, url):
        try:
            response = self._req.get(url)
            return response.status_code
        except Exception:
            return 0
```
Replace the sample2 function when testing the sample1 function.
Test code
tests/test_script.py
from src.script import sample1
def test_sample1_mock_sample2_200(mocker):
    """
Make the sample2 function return 200.
    """
    status_code = 200
    url = "https://hogehoge.com"
    mocker.patch("src.script.sample2", return_value=status_code)
    assert sample1(url) == status_code
def test_sample1_mock_sample2_404(mocker):
    """
Make the sample2 function return 404.
    """
    status_code = 404
    url = "https://fugafuga.com"
    mocker.patch("src.script.sample2", return_value=status_code)
    assert sample1(url) == status_code
Execution result
 > pytest
======================== test session starts ========================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\root
plugins: cov-2.10.0, mock-3.3.1
collected 2 items
tests\test_script.py ..                                        [100%] 
========================= 2 passed in 0.19s =========================
Test code
tests/test_script.py
from src.script import sample1
def test_sample1_mock_sample2(mocker):
    """
sample2 function is 200 by url,404,Try to return one of 0.
    """
    def return_status_code(url):
        """
200 by url,404,Returns one of 0
        """
        if url == "https://hogehoge.com":
            return 200
        elif url == "https://fugafuga.com":
            return 404
        else:
            return 0
    mocker.patch("src.script.sample2", side_effect=return_status_code)
    assert sample1("https://hogehoge.com") == 200
    assert sample1("https://fugafuga.com") == 404
    assert sample1("https://higehige.com") == 0
Execution result
 > pytest
======================== test session starts ========================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\root
plugins: cov-2.10.0, mock-3.3.1
collected 1 item
tests\test_script.py .                                         [100%] 
========================= 1 passed in 0.20s =========================
Test code
tests/test_script.py
import pytest
from src.script import sample1
def test_sample1_mock_sample2_exception(mocker):
    """
Raise an Exception when the sample2 function is called.
    """
    url = "https://hogehoge.com"
    mocker.patch("src.script.sample2", side_effect=Exception)
    with pytest.raises(Exception):
        sample1(url)
Execution result
 > pytest
======================== test session starts ========================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\root
plugins: cov-2.10.0, mock-3.3.1
collected 1 item
tests\test_script.py .                                         [100%] 
========================= 1 passed in 0.17s =========================
Replace the get function in the request module when testing the sample2 function.
Test code
tests/test_script.py
import requests
from src.script import sample2
def test_sample2_mock_requests_200(mocker):
    """
requests executed by the sample2 function.Replace get.
Also, the return value is also status in Mock_Replace code so that it returns 200.
    """
    status_code = 200
    url = "https://hogehoge.com"
    response_mock = mocker.Mock()
    response_mock.status_code = status_code
    mocker.patch.object(requests, "get", return_value=response_mock)
    assert sample2(url) == status_code
def test_sample2_mock_requests_404(mocker):
    """
requests executed by the sample2 function.Replace get.
Also, the return value is also status in Mock_Replace code so that it returns 404.
    """
    status_code = 404
    url = "https://fugafuga.com"
    response_mock = mocker.Mock()
    response_mock.status_code = status_code
    mocker.patch.object(requests, "get", return_value=response_mock)
    assert sample2(url) == status_code
Execution result
 > pytest
======================== test session starts ========================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\root
plugins: cov-2.10.0, mock-3.3.1
collected 1 item
tests\test_script.py ..                                        [100%] 
========================= 2 passed in 0.22s =========================
Test code
tests/test_script.py
import requests
from src.script import sample2
def test_sample2_mock_request_exception(mocker):
    """
requests with sample2 function.Raise an Exception when get is called.
    """
    status_code = 0
    url = "https://hogehoge.com"
    mocker.patch.object(requests, "get", side_effect=Exception)
    assert sample2(url) == status_code
Execution result
 > pytest
======================== test session starts ========================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\root
plugins: cov-2.10.0, mock-3.3.1
collected 1 item
tests\test_script.py .                                         [100%] 
========================= 1 passed in 0.19s =========================
When testing the get method of the SampleClass class, replace the get function of _req called inside.
Test code
tests/test_script.py
from src.script import SampleClass
class TestSampleClass:
    def test_get_200(self, mocker):
        """
Called by get method of SampleClass_req.Replace get.
Also, the return value is also status in Mock_Replace code so that it returns 200.
        """
        status_code = 200
        url = "https://hogehoge.com"
        sample = SampleClass()
        response_mock = mocker.Mock()
        response_mock.status_code = status_code
        req_mock = mocker.MagicMock()
        req_mock.get = mocker.Mock(return_value=response_mock)
        mocker.patch.object(sample, "_req", req_mock)
        assert sample.get(url) == status_code
Execution result
 > pytest
======================== test session starts ========================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\root
plugins: cov-2.10.0, mock-3.3.1
collected 1 item
tests\test_script.py .                                         [100%] 
========================= 1 passed in 0.21s =========================
Test code
tests/test_script.py
from src.script import SampleClass
class TestSampleClass:
    def test_get_exception(self, mocker):
        """
With the get method of SampleClass_req.Raise an Exception when get is called.
        """
        status_code = 0
        url = "https://hogehoge.com"
        sample = SampleClass()
        req_mock = mocker.MagicMock()
        req_mock.get = mocker.Mock(side_effect=Exception)
        mocker.patch.object(sample, "_req", req_mock)
        assert sample.get(url) == status_code
Execution result
 > pytest
======================== test session starts ========================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\root
plugins: cov-2.10.0, mock-3.3.1
collected 1 item
tests\test_script.py .                                         [100%] 
========================= 1 passed in 0.21s =========================
I just researched various things and tried it myself, so I don't know if it's the correct way to use it.
Recommended Posts