pytest-mock | wrapper around the mock package | Mock library
kandi X-RAY | pytest-mock Summary
Support
Quality
Security
License
Reuse
Currently covering the most popular Java, JavaScript and Python libraries. See a Sample Here
pytest-mock Key Features
pytest-mock Examples and Code Snippets
Trending Discussions on pytest-mock
Trending Discussions on pytest-mock
QUESTION
I'm using pytest-mocker to patch a function to mock away what it's doing. But I also want to know how many times it was called and its call args.
Some script:
def do_something():
x = 42
return calculate(x)
def calculate(num):
return num * 2
The test:
def test_do_something(mocker):
mocker.patch("sb.calculate", return_value=1)
result = do_something()
assert result == 1 # Assert calculate is mocked correctly and returned whatever what I set above
This helped me mock away the call but I want to know during the test how many times calculate was called and its call arguments. Struggling to see how to do this.
ANSWER
Answered 2022-Apr-08 at 22:26You can do it like this:
mock_calculate = mocker.patch("sb.calculate", return_value=1)
assert mock_calculate.call_count == 1
Or if you want to check what parameters were passed:
mock_calculate = mocker.patch("sb.calculate", return_value=1)
assert mock_calculate.called_once_with(1)
QUESTION
Suppose I have a module that checks a date is today (this is a simplified code)
# my_module.py
import datetime
def is_today(input_date):
return input_date == datetime.date.today()
I want to test this function
# test_my_module.py
import datetime
from unittest.mock import patch
from my_module import is_today
def test_is_today():
cases = [
{'input_date': datetime.date(2022, 5, 14), 'expected_output': True, 'today': datetime.date(2022, 5, 14)},
{'input_date': datetime.date(2022, 5, 14), 'expected_output': False, 'today': datetime.date(2022, 5, 12)}
]
for case in cases:
with patch('my_module.datetime.date.today', lambda x: case['today']):
assert is_today(case['input_date']) == case['expected_output']
But when I run pytest test_my_module.py
, I get FAILED test_my_module.py::test_is_today - TypeError: can't set attributes of built-in/extension type 'datetime.date'
Any idea how to do it?
Also does anybody know how to do it with pytest-mock and which method is prefered?
ANSWER
Answered 2022-Apr-08 at 21:23You could patch the entire datetime
:
for case in cases:
class FakeDatetime:
class date:
def today(): return case['today']
with patch('my_module.datetime', FakeDatetime)
The limitation is that you lose all other functionality of datetime
.
QUESTION
I'm trying to mock chained call in pytest
(and pytest-mock
package), but for some unknown (for me) reason it doesn't work which means I don't get mocked return_value
from my mock. It looks like it created object of MagicMock
, but without defined return_value
. My guess is that it's related to chained call.
class DatabaseService():
def __init__(self):
self.db = next(get_db())
self.categories = {}
def save(self, product_id):
if not self.categories:
self._load_existing_categories()
...
[rest of the code]
def _load_existing_categories(self):
# result = self.db.execute(select(Category).order_by(Category.id)).all() <-- This is my original line, but I can't make it working even with simplified version (line below)
result = self.db.execute().all()
for obj in result:
print("Result")
print(obj)
self.categories[obj.Category.name] = obj.Category
Here is my test:
def test_load_from_database(mocker):
session = mocker.patch('src.mysql.get_db')
session.execute.all.return_value = ['1st element', '2nd element']
# I tried also mock it by these ways:
# session = mocker.MagicMock()
# session.session.execute().all().return_value = ['1st element', '2nd element']
# session.get_db.execute().all().return_value = ['1st element', '2nd element']
# session.execute.all = ['1st element', '2nd element']
service = DatabaseService()
service.save = mocker.MagicMock(side_effect=service.save)
service.db = session
service.save(123)
[here will be some assert once I make it working]
For some reason, mock of self.db.execute().all()
doesn't return anything. The code inside loop is not executed even single time.
When I print print(result)
I get:
But it looks like return_value
is not assigned to it, so either it's not the same object or value is removed somehow.
ANSWER
Answered 2022-Feb-22 at 19:38Your code calls this:
self.db.execute().all()
In your test you are setting this:
session.execute.all.return_value
I think you should set:
session.execute.return_value.all.return_value
because your code calls execute, so you need to set up mocks on its return value.
QUESTION
I am following this mini-tutorial/blog on pytest-mock. I can not understand how the mocker
is working since there is no import for it - in particular the function declaration def test_mocking_constant_a(mocker):
import mock_examples.functions
from mock_examples.functions import double
def test_mocking_constant_a(mocker):
mocker.patch.object(mock_examples.functions, 'CONSTANT_A', 2)
expected = 4
actual = double() # now it returns 4, not 2
assert expected == actual
Somehow the mocker
has the attributes/functions of pytest-mocker.mocker
: in particular mocker.patch.object
. But how can that be without the import statement?
ANSWER
Answered 2022-Feb-18 at 08:27The mocker
variable is a Pytest fixture. Rather than using imports, fixtures are supplied using dependency injection - that is, Pytest takes care of creating the mocker object for you and supplies it to the test function when it runs the test.
Pytest-mock defines the "mocker" fixture here, using the Pytest fixture
decorator. Here, the fixture
decorator is used as a regular function, which is a slightly unusual way of doing it. A more typical way of using the fixture
decorator would look something like this:
@pytest.fixture()
def mocker(pytestconfig: Any) -> Generator[MockerFixture, None, None]:
"""
Return an object that has the same interface to the `mock` module, but
takes care of automatically undoing all patches after each test method.
"""
result = MockerFixture(pytestconfig)
yield result
result.stopall()
The fixture
decorator registers the "mocker" function with Pytest, and when Pytest runs a test with a parameter called "mocker", it inserts the result of the "mocker" function for you.
Pytest can do this because it uses Python's introspection features to view the list of arguments, complete with names, before calling the test function. It compares the names of the arguments with names of fixtures that have been registered, and if the names match, it supplies the corresponding object to that parameter of the test function.
QUESTION
I am new to Python development, I am writing test cases using pytest where I need to mock some behavior. Googling best mocking library for pytest, has only confused me. I have seen unittest.mock, mock, mocker and pytest-mock. Not really sure which one to use.Can someone please explain me the difference between them and also recommend me one?
ANSWER
Answered 2022-Jan-28 at 05:18So pytest-mock is a thin wrapper around mock and mock is since python 3.3. actually the same as unittest.mock. I don't know if mocker is another library, I only know it as the name of the fixture provided by pytest-mock to get mocking done in your tests. I personally use pytest and pytest-mock for my tests, which allows you to write very concise tests like
from pytest_mock import MockerFixture
@pytest.fixture(autouse=True)
def something_to_be_mocked_everywhere(mocker):
mocker.patch()
def tests_this(mocker: MockerFixture):
mocker.patch ...
a_mock = mocker.Mock() ...
...
But this is mainly due to using fixtures, which is already pointed out is what pytest-mock offers.
QUESTION
I'm learning to use mocking while writing unit tests. But my tests always take the time it would take if I was not mocking them. Consider this example function:
application.py
from time import sleep
def is_windows():
# fake slow operation
sleep(5)
return True
def get_operating_system():
return 'Windows' if is_windows() else 'Linux'
application_test.py
from application import get_operating_system
# 'mocker' fixture provided by pytest-mock
def test_get_operating_system(mocker):
# Mock the slow function to return True
mocker.patch('application.is_windows', return_value=True)
assert get_operating_system() == 'Windows'
For some reason this test always takes more than 5 seconds to run. If I remove sleep(5)
from is_windows()
then the test runs in under a second, so it looks like the function isn't being mocked at all! Am I doing something wrong here? Any help appreciated. Thanks.
EDIT: I'm running my tests from PyCharm.
ANSWER
Answered 2022-Jan-21 at 10:39You need to import application
if you want to mock something in it.
import application
def test_get_operating_system(mocker):
# Mock the slow function to return True
mocker.patch('application.is_windows', return_value=True)
assert application.get_operating_system() == 'Windows'
Otherwise, you aren't using the patch.
edit: Actually, maybe I'm crazy about that. You code mocks it as expected. I have run it on my machine and it works correctly.
outputsno patch:
1 passed in 5.03s
with mocker.patch("application.is_windows", return_value=True)
:
1 passed in 0.02s
with mocker.patch("application.is_windows", return_value=False)
:
1 failed in 0.10s
QUESTION
I'm trying to mock a class method with pytest-mock. I have the code below in a single file, and when the test is run I get ModuleNotFoundError: No module named 'RealClass'
in the patch
function. How to make this work?
class RealClass:
def some_function():
return 'real'
def function_to_test():
x = RealClass()
return x.some_function()
def test_the_function(mocker):
mock_function = mocker.patch('RealClass.some_function')
mock_function.return_value = 'mocked'
ret = function_to_test()
assert ret == 'mocked'
ANSWER
Answered 2022-Jan-13 at 01:58In your case since you are patching the class that is present within the test file itself you would use mocker.patch.object
.
mock_function = mocker.patch.object(RealClass, 'some_function')
collected 1 item
tests/test_grab.py::test_the_function PASSED [100%]
============================== 1 passed in 0.03s ===============================
QUESTION
I have a issue with one of my fixtures, which is doing a patch not resetting between test calls.
The fixture is basically a patch which is wrapper around a object, so I can assert that it has been passed into another function.
The fixture looks like this:
@pytest.fixture
def mock_entities(mocker: MockFixture) -> MagicMock:
entities = Entities()
namespace = f"{__name__}.{Entities.__name__}"
return mocker.patch(namespace, return_value=entities)
Entities
is a class that I want to patch, but I want it to function completely like the original, due to the fact that it has property
methods, as well as using __len__
. It is declared in the body of a function and the reason I need it mocked is because I'm passing it into another function and I want to assert that it has been passed in correctly. I originally tried "wraps=`, but I wasn't able to get that to work correctly.
Full test code below:
import pytest
from pytest_mock import MockFixture
from unittest.mock import MagicMock, PropertyMock
from typing import List
from pprint import pprint
from unittest.mock import patch
class Entities:
_entities: List[dict] = []
def __init__(self, entities: List[dict] = []):
self._entities = entities
@property
def entities(self) -> List[dict]:
return self._entities
@entities.setter
def entities(self, value: List[dict]):
self._entities = value
def append(self, value: dict):
self._entities.append(value)
def __len__(self) -> int:
return len(self._entities)
class ApiClient:
def get_values(self) -> List[dict]:
# We get values from a API with a pager mechanism here
pass
class EntitiesCacheClient:
def get_values(self) -> Entities:
# We get values from cache here
pass
def set_values(sel, values: Entities):
# We set values to cache here
pass
class EntityDataSource:
_api_client: ApiClient = None
_cache_client: EntitiesCacheClient = None
def __init__(self) -> None:
self._api_client = ApiClient()
self._cache_client = EntitiesCacheClient()
def get_entities(self) -> Entities:
entities = self._get_entities_from_cache()
if entities:
return entities
# I want to mock Entities, so that I can assert that it is passed in to the EntitiesCacheClient.set_values()
entities = Entities()
api_values = 1
while api_values:
api_values = self._api_client.get_values()
if not api_values:
break
for values in api_values:
entities.append(values)
if entities:
self._save_entities_to_cache(entities)
return entities
def _get_entities_from_cache(self) -> Entities:
return self._cache_client.get_values()
def _save_entities_to_cache(self, entities: Entities):
self._cache_client.set_values(entities)
@pytest.fixture
def mock_entities_cache_client(mocker: MockFixture) -> MagicMock:
namespace = f"{__name__}.{EntitiesCacheClient.__name__}"
return mocker.patch(namespace, autospec=True).return_value
@pytest.fixture
def mock_api_client(mocker: MockFixture) -> MagicMock:
namespace = f"{__name__}.{ApiClient.__name__}"
return mocker.patch(namespace, autospec=True).return_value
@pytest.fixture
def mock_entities(mocker: MockFixture) -> MagicMock:
entities = Entities()
namespace = f"{__name__}.{Entities.__name__}"
return mocker.patch(namespace, return_value=entities)
def test_entity_data_source_entities(mock_entities_cache_client, mock_api_client, mock_entities):
mock_entities_cache_client.get_values.return_value = None
expected_entity_1 = {"id": 1, "data": "Hello"}
expected_entity_2 = {"id": 2, "data": "World"}
expected_entities_list = [
expected_entity_1, expected_entity_2
]
mock_api_client.get_values.side_effect = [
[
expected_entity_1,
expected_entity_2,
],
[]
]
entity_data_source = EntityDataSource()
result: Entities = entity_data_source.get_entities()
mock_entities_cache_client.set_values.assert_called_once_with(mock_entities.return_value)
assert len(result.entities) == len(expected_entities_list)
assert result.entities == expected_entities_list
def test_entity_data_source_entities_more_results(mock_entities_cache_client, mock_api_client, mock_entities):
mock_entities_cache_client.get_values.return_value = None
expected_entity_1 = {"id": 1, "data": "Hello"}
expected_entity_2 = {"id": 2, "data": "World"}
expected_entity_3 = {"id": 3, "data": "How"}
expected_entity_4 = {"id": 4, "data": "Are"}
expected_entity_5 = {"id": 5, "data": "You"}
expected_entity_6 = {"id": 6, "data": "Doing?"}
expected_entities_list = [
expected_entity_1, expected_entity_2, expected_entity_3,
expected_entity_4, expected_entity_5, expected_entity_6
]
mock_api_client.get_values.side_effect = [
[
expected_entity_1,
expected_entity_2,
expected_entity_3,
expected_entity_4,
expected_entity_5,
],
[expected_entity_6],
[]
]
entity_data_source = EntityDataSource()
result: Entities = entity_data_source.get_entities()
mock_entities_cache_client.set_values.assert_called_once_with(mock_entities.return_value)
assert len(result.entities) == len(expected_entities_list)
assert result.entities == expected_entities_list
On the second test method, the fixture is patching Entities
and it has a return_value=Entities()
(basically). However, the fixture/mock seems to retain the original Entities
from the first test, meaning that it already has 2 records inside the _entities
, resulting in a total of 8 records and not the 6 it should have.
> assert len(result.entities) == len(expected_entities_list)
E assert 8 == 6
E -8
E +6
Why is this happening? I thought when using the pyest-mock
and the mocker
fixture, there would be no need to reset the mocks as it takes care of that for you
https://pypi.org/project/pytest-mock/
This plugin provides a mocker fixture which is a thin-wrapper around the patching API provided by the mock package. Besides undoing the mocking automatically after the end of the test, it also provides other nice utilities such as
spy
andstub
, and uses pytest introspection when comparing calls.
Does this not extend to the objects assigned to return_value
? How am I supposed to be mocking Entities
if this is not the correct way?
ANSWER
Answered 2022-Jan-03 at 20:07You have been a victim of the common pitfull of mutable default arguments. Each time you set the entities
property you in fact change the default value of the entities
argument, so the next time a new Entities
object will be created with an empty argument, this will be used instead of an empty list.
The usual fix is to use a non-mutable placeholder object as default value:
def __init__(self, entities: List[dict] = None):
self._entities = entities or []
If you are interested in the reasons for this design decision, you can check these related questions:
QUESTION
I am trying to use pytest-mock to mock chained function calls.
@app.route('/application', methods=['PUT'])
def update_application():
a = json.loads(request.data)['application']
application = Application.objects(id=a['id']).first()
if not application:
return jsonify({'error': 'data not found'})
else:
application.update(jobTitle=a['jobTitle'],
companyName=a['companyName'],
date=a['date'],
status=a['status'
The method update_application
has a chained function call like below
Application.objects(id=a['id']).first()
I have tried mocking the chained calls using below approach but I could not mock.
def test_update_application(client, mocker):
application = Application(id=4, jobTitle='fakeJob12345', companyName='fakeCompany', date=str(datetime.date(2021, 9, 22)))
mocker.patch(
'app.Application.update'
)
m = mocker.MagicMock()
m.Application.objects().first.return_value = application
rv = client.put('/application', json={'application':{
'id':2, 'jobTitle':'fakeJob12345', 'companyName':'fakeCompany', 'date':str(datetime.date(2021, 9, 23)), 'status':'1'
}})
print(rv.data)
jdata = json.loads(rv.data.decode("utf-8"))["jobTitle"]
assert jdata == 'fakeJob12345'
I could mock Application.objects(id=a['id'])
like below which worked fine.
mocker.patch(
'app.Application.objects',
return_value = list_application
)
Can anyone point me on how to mock chained functions?
ANSWER
Answered 2021-Nov-01 at 08:03Here is how you could do this - I only changed the 3 code lines dealing with the mock:
def test_update_application(client, mocker):
application = Application(id=4, jobTitle='fakeJob12345', companyName='fakeCompany', date=str(datetime.date(2021, 9, 22)))
mock_objects = mocker.MagicMock(name='objects')
mocker.patch('app.Application.objects', new=mock_objects)
mock_objects.return_value.first.return_value = application
rv = client.put('/application', json={'application':{
'id':2, 'jobTitle':'fakeJob12345', 'companyName':'fakeCompany', 'date':str(datetime.date(2021, 9, 23)), 'status':'1'
}})
print(rv.data)
jdata = json.loads(rv.data.decode("utf-8"))["jobTitle"]
assert jdata == 'fakeJob12345'
It's important to understand that you are mocking the objects
function of the Application
class, like so: mocker.patch('app.Application.objects', new=mock_objects)
.
Then, you need to set the return value: mock_objects.return_value.first.return_value = application
.
The first part mock_objects.return_value
is matching the object returned in Application.objects(id=a['id'])
, then you need to append .first.return_value = application
which matches the return value of .first()
QUESTION
I have an anaconda environment that has Python 3.7 installed. I have a file with some imports out of order which I want VScode to order when pressing CRTL+s
.
However, instead or ordering the imports, there is a crash and nothing happens.
ProblemWhen I press CRTL+s
on my VScode, I get a pop up saying the Python extension crashes. After some investigation, this is the stack-trace I found:
Error 2021-10-15 16:00:05: Traceback (most recent call last):
File "/home/user/.vscode/extensions/ms-python.python-2021.10.1336267007/pythonFiles/sortImports.py", line 12, in
Traceback (most recent call last):
File "/home/user/.vscode/extensions/ms-python.python-2021.10.1336267007/pythonFiles/sortImports.py", line 12, in
Error 2021-10-15 16:00:05: Failed to format imports for '/home/user/Workplace/work/my-project/cars/client.py'. Traceback (most recent call last):
File "/home/user/.vscode/extensions/ms-python.python-2021.10.1336267007/pythonFiles/sortImports.py", line 12, in
Error 2021-10-15 16:00:05: import isort.main
File "/home/user/.vscode/extensions/ms-python.python-2021.10.1336267007/pythonFiles/lib/python/isort/__init__.py", line 21, in
from . import settings
File "/home/user/.vscode/extensions/ms-python.python-2021.10.1336267007/pythonFiles/lib/python/isort/settings.py", line 5, in
import configparser
File "/home/user/anaconda3/envs/my-project/lib/python3.7/configparser.py", line 147, in
import re
File "/home/user/anaconda3/envs/my-project/lib/python3.7/re.py", line 124, in
import enum
File "/home/user/anaconda3/envs/my-project/lib/python3.7/enum.py", line 2, in
from types import MappingProxyType, DynamicClassAttribute
File "/home/user/Workplace/work/my-project/cars/types.py", line 1, in
from pathlib import Path
File "/home/user/anaconda3/envs/my-project/lib/python3.7/pathlib.py", line 13, in
from urllib.parse import quote_from_bytes as urlquote_from_bytes
File "/home/user/anaconda3/envs/my-project/lib/python3.7/urllib/parse.py", line 627, in
_asciire = re.compile('([\x00-\x7f]+)')
AttributeError: module 're' has no attribute 'compile'
I don't see any issues with my setup. For more information, this is my conda
env setup:
name: my-project
channels:
- pytorch
- defaults
dependencies:
- _libgcc_mutex=0.1=main
- _openmp_mutex=4.5=1_gnu
- blas=1.0=mkl
- ca-certificates=2021.9.30=h06a4308_1
- certifi=2021.10.8=py37h06a4308_0
- cpuonly=1.0=0
- intel-openmp=2021.3.0=h06a4308_3350
- ld_impl_linux-64=2.35.1=h7274673_9
- libffi=3.3=he6710b0_2
- libgcc-ng=9.3.0=h5101ec6_17
- libgomp=9.3.0=h5101ec6_17
- libstdcxx-ng=9.3.0=hd4cf53a_17
- libuv=1.40.0=h7b6447c_0
- mkl=2021.3.0=h06a4308_520
- ncurses=6.2=he6710b0_1
- ninja=1.10.2=hff7bd54_1
- openssl=1.1.1l=h7f8727e_0
- pip=21.2.2=py37h06a4308_0
- pytest-mock=3.6.1=pyhd3eb1b0_0
- python=3.7.11=h12debd9_0
- pytorch=1.9.1=py3.7_cpu_0
- readline=8.1=h27cfd23_0
- setuptools=58.0.4=py37h06a4308_0
- sqlite=3.36.0=hc218d9a_0
- tk=8.6.11=h1ccaba5_0
- torchaudio=0.9.1=py37
- typing-extensions=3.10.0.2=hd3eb1b0_0
- typing_extensions=3.10.0.2=pyh06a4308_0
- wheel=0.37.0=pyhd3eb1b0_1
- xz=5.2.5=h7b6447c_0
- zlib=1.2.11=h7b6447c_3
- pip:
- argcomplete==1.12.3
- attrs==21.2.0
- babel==2.9.1
- beautifulsoup4==4.10.0
- black==21.9b0
- charset-normalizer==2.0.4
- click==7.1.2
- colorama==0.4.4
- colorclass==2.2.0
- commonmark==0.9.1
- dataclasses==0.6
- docutils==0.16
- humanize==3.11.0
- idna==3.2
- imagesize==1.2.0
- importlib-metadata==4.8.1
- iniconfig==1.1.1
- jinja2==3.0.1
- m2r==0.2.1
- markupsafe==2.0.1
- mistune==0.8.4
- mypy==0.910
- mypy-extensions==0.4.3
- nodeenv==1.6.0
- numpy==1.21.2
- packaging==21.0
- pathspec==0.9.0
- pillow==8.3.2
- platformdirs==2.4.0
- pluggy==1.0.0
- py==1.10.0
- pygments==2.10.0
- pyparsing==2.4.7
- pyright==0.0.10
- pytest==6.2.5
- pytest-describe==2.0.0
- pytz==2021.1
- pyyaml==5.4.1
- regex==2021.10.8
- requests==2.26.0
- requests-toolbelt==0.9.1
- responses==0.13.4
- rich==10.12.0
- six==1.16.0
- snowballstemmer==2.1.0
- soupsieve==2.2.1
- tests==0.7
- toml==0.10.2
- tomli==1.2.1
- torch==1.9.1+cpu
- typed-ast==1.4.3
- types-pyyaml==5.4.12
- types-requests==2.25.11
- upolygon==0.1.6
- urllib3==1.26.6
- zipp==3.5.0
variables:
PYTHONPATH: .
prefix: /home/user/anaconda3/envs/my-project
At first I thought it could be a dependency issue, so I started removing every extra dependency I could find.
However, even after doing it, My vscode extension is still not working.
Questions- What is wrong with my setup?
- How can I fix it?
ANSWER
Answered 2021-Oct-27 at 09:34The problem here is that I had broken dependencies which would not allow me to do any updates nor new installs.
This had to do with having packages from both conda
and pip
. Some of them play nice together, some don't.
My solution, was unfortunately, rather atomic. I deleted the environment and created a new one with Python 3.7. Upon doing that, I also added an extra conda channel conda-forge
which I recommend instead of pip
.
Once I did that I installed all the dependencies and packages using conda
and it worked.
Here are the command I used:
conda create -n my-project python=3.7
conda config --env --add channels conda-forge
conda env config vars set PYTHONPATH="."
conda deactivate
conda activate my-project
And then conda isntall ...
the packages you need.
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
Vulnerabilities
No vulnerabilities reported
Install pytest-mock
You can use pytest-mock like any standard Python library. You will need to make sure that you have a development environment consisting of a Python distribution including header files, a compiler, pip, and git installed. Make sure that your pip, setuptools, and wheel are up to date. When using pip it is generally recommended to install packages in a virtual environment to avoid changes to the system.
Support
Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from over 650 million Knowledge Items
Find more librariesExplore Kits - Develop, implement, customize Projects, Custom Functions and Applications with kandi kits
Save this library and start creating your kit
Share this Page