test a function called twice in python
@patch('call_me')
def test_func(self,mock_call_me):
self.assertEqual(func(),None)
self.assertEqual(mock_call_me.call_count, 2)
How to mock functions with same name, when called twice?
I tried to reproduce your codes, but for me client.graphql
got mocked perfectly.
Here are my codes.
Folder structure
├── code_file.py
├── code_test.py
└── prefect.py
# prefect.py
class Client:
def __init__(self, *arg, **kwargs):
...
def graphql(self, *args, **kwargs):
print(f"graphql got called with args: {args}, kwargs: {kwargs}")
def create_flow(self, *args, **kwargs):
print(f"create_flow got called with args: {args}, kwargs: {kwargs}")
# code_file.py
from prefect import Client
graphql_value1 = "graphql_value1"
graphql_value2 = "graphql_value2"
client = Client(api_server="http://example.com")
def run_flow(name1, name2):
# some graphql queries, not shown here
value1 = client.graphql(graphql_value1, variables={"name": name1})
print("First client call return value:- ", value1)
value2 = client.graphql(graphql_value2, variables={"name": name2, "id": value1["data"][0]["id"]})
print("Second client call return value:- ", value2)
run_id = client.create_flow(id=value2["data"][0]["id"])
return run_id
# code_test.py
import unittest
from unittest.mock import patch, MagicMock, call
from code_file import run_flow
class TestFlow(unittest.TestCase):
@patch("code_file.client.graphql")
@patch("code_file.client.create_flow")
def test_flow(self, create_flow: MagicMock, graphql: MagicMock) -> None:
first_graphql_return_value = {"data": [{"id": 1}]}
second_graphl_return_value = {"data": [{"id": 2}]}
graphql.side_effect = [
first_graphql_return_value,
second_graphl_return_value,
]
create_flow.return_value = "test_id"
self.assertEqual(run_flow("name1", "name2"), "test_id")
create_flow.assert_called_once_with(id=2)
graphql.assert_has_calls(
[
call("graphql_value1", variables={"name": "name1"}),
call("graphql_value2", variables={"name": "name2", "id": 1})
]
)
Running unittest by using the command
python -m unittest code_test.py
produces the following output
First client call return value:- {'data': [{'id': 1}]}
Second client call return value:- {'data': [{'id': 2}]}
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
You'll see that the prints in prefect.Client
methods do not get printed.
Original answer
Explanation
From python official documentation
If side_effect is an iterable then each call to the mock will return the next value from the iterable.
When you set side_effect
argument with an iterable, the mock will return next value from the iterable. More detail can be found here
Solution
def test_flow(mocker):
# Some lines of code, not shown here
m = mocker.patch("code_file.client.graphql", side_effect=["value1", "value2"])
...
why is this method being called twice (pytest)
I think the problem is that pytest_bdd lets you use the @given
as fixtures but you also added the @pytest.fixture
decorator on the @when
.
That means the add_properties
function is called once as the when
step and then it is called again as a pytest fixture. Remove the extra decorator and use the original create_tree
fixture instead.
import pytest
from pytest_bdd import scenario, given, when, then, parsers
from models import Tree
@scenario('../features/Tree.feature',
'add properties to a tree')
def test_tree():
pass
@given("a new tree is created", target_fixture="create_tree")
def create_tree():
return Tree()
@when(parsers.cfparse("{properties} are added as properties"))
def add_properties(create_tree, properties):
properties = properties.split(',')
create_tree.add_properties(properties)
return create_tree
@then("I can get these properties")
def publish_article(create_tree):
result = create_tree.get_properties()
assert len(result) == 3
Why a function of python class is called twice
Because you should import modules, not classes. So when you are doing from Test import *
you are running all your code, which includes the instantiation and call to the f
method. And after importing you are instantiating and calling it again, so that's the reason it is being printed twice, because it is being run twice.
Why are my python functions being called twice?
- As noted in the comments, the functions are called twice within
main
- Once at the bottom of
main
- Once in
setRules
- Once at the bottom of
- The functions should not reside in
main
- The functions should be called from
main
, and the valuereturned
, stored as a variable to use insetRules
- The logic is not set to determine the winner between
rock
,paper
orscissor
- The effective scope of
Player
,Computer
andoptions
is global.
def humanPlay() -> str:
response = input("Make a selection between Rock, Paper, Scissors: ", )
response = response.upper()
if response in options:
print(response)
return response
else:
print(f"{response}, is not a valid selection")
return humanPlay()
def computerPlay() -> str:
comp_choice = random.choice(options)
print(comp_choice)
return comp_choice
def setRules(comp_choice: str, human_choice: str):
rockWin = "You Win! ROCK beats SCISSORS "
paperWin = "You Win! PAPER beats ROCK "
scissorsWin = "You Win! SCISSORS beats PAPER "
draw = print("It's a Draw!, computer selected", f"{comp_choice}", "and you selected", f"{human_choice}")
#loser = print(f"You Lose! {human_choice}", f"can't beat {comp_choice}")
Player = "Player"
Computer = "Computer"
options = ["ROCK","PAPER","SCISSORS"]
def main():
h_choice = humanPlay()
c_choice = computerPlay()
setRules(c_choice, h_choice)
main()
Calling TestCase class cause functions to run twice
1. why does the test function run twice?
Because the unittest
code finds two objects in the module's namespace that are unittest.TestCase
classes, MyTestCase
and x
. It doesn't check that these are actually the same object. (Well, it has some awareness of this, in that it doesn't treat x
as a new class to be tested, so it doesn't call setUpClass
again; see "Class and Module Fixtures".)
2. why is the print statement print('test end') not executed?
By default, unittest.main()
calls sys.exit()
after running the tests. You can disable this by adding the argument exit=False
:
if __name__ == "__main__":
print('test start')
unittest.main(exit=False)
print('test end')
Checking whether function has been called multiple times with different parameters
Even @MartinPieters's answer is correct I think that is not the best way to do it. Mock provide assert_has_calls
to do this kind of duties.
Your test could be:
function.assert_has_calls([mock.call(1, 2), mock.call(2, 3)])
Where mock.call
is a helper class do to these kind of jobs.
Pay attention that is a has call and means the call list should be in the list of call and not equal. To solve it I usually define my own helper assert_is_calls()
as follow
def assert_is_calls(m, calls, any_order=False):
assert len(m.mock_calls) == len(calls)
m.assert_has_calls(calls, any_order=any_order)
That a resume example
>>> import mock
>>> f = mock.Mock()
>>> f(1)
<Mock name='mock()' id='139836302999952'>
>>> f(2)
<Mock name='mock()' id='139836302999952'>
>>> f.assert_has_calls([mock.call(1), mock.call(2)])
>>> f.assert_has_calls([mock.call(2), mock.call(1)])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/damico/.local/lib/python2.7/site-packages/mock/mock.py", line 969, in assert_has_calls
), cause)
File "/home/damico/.local/lib/python2.7/site-packages/six.py", line 718, in raise_from
raise value
AssertionError: Calls not found.
Expected: [call(2), call(1)]
Actual: [call(1), call(2)]
>>> f.assert_has_calls([mock.call(2), mock.call(1)], any_order=True)
>>> f(3)
<Mock name='mock()' id='139836302999952'>
>>> f.assert_has_calls([mock.call(2), mock.call(1)], any_order=True)
>>> f.assert_has_calls([mock.call(1), mock.call(2)])
>>> assert len(f.mock_calls)==2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
>>> assert len(f.mock_calls)==3
>>> def assert_is_calls(m, calls, any_order=False):
... assert len(m.mock_calls) == len(calls)
... m.assert_has_calls(calls, any_order=any_order)
...
>>> assert_is_calls(f, [mock.call(1), mock.call(2), mock.call(3)])
>>> assert_is_calls(f, [mock.call(1), mock.call(3), mock.call(2)])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in assert_is_calls
File "/home/damico/.local/lib/python2.7/site-packages/mock/mock.py", line 969, in assert_has_calls
), cause)
File "/home/damico/.local/lib/python2.7/site-packages/six.py", line 718, in raise_from
raise value
AssertionError: Calls not found.
Expected: [call(1), call(3), call(2)]
Actual: [call(1), call(2), call(3)]
>>> assert_is_calls(f, [mock.call(1), mock.call(3), mock.call(2)], True)
>>> assert_is_calls(f, [mock.call(1), mock.call(3)], True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in assert_is_calls
AssertionError
>>>
Related Topics
How to Change the File Name of an Uploaded File in Django
Permissionerror: [Errno 13] Permission Denied
Pandas - Tokenizing Data Expected 1 Field Saw Multiple
How to Make a Grade Calculator in Python
Python Read File Determined by Separator \R\N
Tf.Data.Dataset: How to Get the Dataset Size (Number of Elements in an Epoch)
Regular Expression for Double and Integer Validation
How to Extract Data from Dictionary in the List
Sort Array and Return Original Indexes of Sorted Array
How to Display Index During List Iteration With Django
Append Dataframes Together in for Loop
Most Efficient Way to Find Mode in Numpy Array
How to Install Colorama in Python
How to Remove Carriage Return in a Dataframe
How to Get the Current Ipython/Jupyter Notebook Name
How to Deal With Certificates Using Selenium