I have the class InternalProc, defined as following:
class InternalProc:
@staticmethod
def get_data():
try:
result = subprocess.run(['bridge-client', '--business-credentials'],
stdout=subprocess.PIPE)
data = json.loads(result.stdout.decode('utf-8'))
return data
except Exception:
logger.error("Unable to fetch the data")
raise
I want to unit test the get_data()
, how should I mock subprocess.run
? I also want to assert on whether the exception was raised or not.
Seems like you need two tests to test this method, one that returns data and one that raises an Exception
.
For the one where we return data we simply need to mock subprocess.run
. Then we can create another Mock
object to mock stdout.decode
to return data that json.loads
can parse. This means creating a Mock
object for the behavior of stdout
and then configuring our mocked subprocess.run
to use this object.
For the other test we simply need to use the side_effect
kwarg of the Mock
object to raise an exception when it is called.
Given the following folder structure:
stackoverflow/
├── mypackage
│ ├── __init__.py
│ └── proc.py
└── tests
├── __init__.py
└── test_proc.py
The tests we write are shown below.
from unittest.mock import MagicMock, patch
import pytest
from mypackage.proc import InternalProc
@patch("mypackage.proc.subprocess.run")
def test_get_data_valid(mock_run):
mock_stdout = MagicMock()
mock_stdout.configure_mock(
**{
"stdout.decode.return_value": '{"A": 3}'
}
)
mock_run.return_value = mock_stdout
result = InternalProc.get_data()
assert result == {"A": 3}
@patch("mypackage.proc.subprocess.run", side_effect=Exception("foobar"))
def test_get_data_invalid(mock_run):
with pytest.raises(Exception) as exc:
InternalProc.get_data()
assert "foobar" in str(exc.value)
Output:
======================================= test session starts ========================================
platform darwin -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: ***/stackoverflow
collected 2 items
tests/test_proc.py .. [100%]
======================================== 2 passed in 0.07s =========================================
My suggestion to you, since I have seen you post multiple pytest
/mock
questions in the last few days is to spend some time reading the documentation for both. Make some toy examples and play around with the two packages. That is the only way you will learn how to mock and where to mock.