[Pytest 101] 02 Basic Usage of Mock
How to use Mock to simulate behavior, raise exceptions, and test side effects.

After covering the basic tools, this article demonstrates how to use mocks effectively in Pytest.
Mocks allow us to replace the logic of real functions to simulate different scenarios and write more precise unit tests.
We’ll walk through examples of mocking functions, simulating errors, and even handling external dependencies like Redis.
Use mocker.patch
to replace functions in the same module or in external modules.
For example, to mock the function do_something
from service.my_service
:
mocker.patch("service.my_service.do_something", return_value="hello")
Mock function
# my_project/service/do_service.py
def do_something():
try:
response = something()
return response
except Exception as e:
return str(e)
def something():
return "success"
We mock the something()
function and set its return_value
.
To verify the mock worked as expected, we return the value directly and use assert
to confirm the result.
# my_project/test/service/test_do_service.py
import pytest
from pytest_mock import MockFixture
from service.do_service import (
do_something,
)
def test_do_something_success(mocker: MockFixture):
success_message = "success"
mocker.patch("service.do_service.something", return_value=success_message)
result = do_something()
assert result == success_message
Mock function from other module
As projects grow in complexity, it’s common to extract shared functions into separate modules.
Suppose the something()
function now lives in service/do_trick.py
, and is imported in do_service.py
:
# my_project/service/do_trick.py
def something():
return "success"
# my_project/service/do_service.py
from service.do_trick import something
def do_something():
return something()
In this case, the mocking process is slightly different in one crucial way:
When using from package_name import function_name
, the imported function becomes part of the current module.
So instead of mocking service.do_trick.something
, you must mock service.do_service.something
.
If you mock the original module instead, your mock will have no effect—because the function has already been imported into the current scope.
# my_project/test/service/test_do_service.py
import pytest
from pytest_mock import MockFixture
from service.do_service import (
do_something,
)
def test_do_something(mocker: MockFixture):
mocker.patch("service.do_service.something", return_value="not success")
assert do_something() == "not success"
In this example, we mock something()
inside service.do_service
, not the original service.do_trick
.
Mock try-except
When the code includes a try-except
block, you can use a mock to simulate an exception being raised.
Set the side_effect
of the mocked function to an Exception
to simulate an error scenario:
def test_do_something_error(mocker: MockFixture):
error_message = "Exception error"
mocker.patch("service.do_service.something", side_effect=Exception(error_message))
result = do_something()
assert result == error_message
The return_value
attribute only lets you define return values.
For more complex behavior—like raising an exception—use side_effect
, which accepts a function, an iterable, or an exception class
.
Mock raise Exception
Sometimes the code doesn’t raise errors on its own, but you want to intentionally trigger an exception under certain conditions:
# my_project/service/do_service.py
def do_something(err):
if err:
raise Exception(f"Something went wrong")
return "success"
You can use pytest.raises()
to declare the expected exception type and message:
# my_project/test/service/test_do_service.py
import pytest
from pytest_mock import MockFixture
from service.test_do_service import (
do_something,
)
def test_do_something_success():
assert do_something(err=False) == "success"
def test_do_something_err():
with pytest.raises(Exception, match=f"Something went wrong"):
do_something(err=True)
Mock Redis
Since
redis.get
returns abytes
object, you need to useencode
anddecode
.
def get_from_redis(id:int):
# connect to redis
redis = Redis(host=os.environ["REDIS_HOST"])
byte_data = redis.get("mykey.{}".format(id))
data = byte_data.decode("utf-8")
# set key to redis and expire after 1 hour
redis.set(
"mykey.{}".format(id),
data,
ex=3600,
)
return data
def test_get_from_redis(mocker: MockFixture):
test_data = "test_data".encode("utf-8")
mock_redis = mocker.patch("service.do_service.Redis")
mock_redis_instance = mock_redis.return_value
mock_redis_instance.get.return_value = test_data
mock_redis_instance.set.return_value = None
result = get_from_redis(1)
assert result == test_data.decode("utf-8")