Test a Function Called Twice in Python

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
  • The functions should not reside in main
  • The functions should be called from main, and the value returned, stored as a variable to use in setRules
  • The logic is not set to determine the winner between rock, paper or scissor
  • The effective scope of Player, Computer and options 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



Leave a reply



Submit