Skip to content
Advertisement

How to mock functions with same name, when called twice?

How do I mock a function which has been called twice in the same file, with different parameters? Here is my code snippet:

code_file.py

from prefect import Client
client = Client(
    api_server = <ip of server>
)
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

# I want to mock these client calls.
# However when I am mocking, only the first client.graphql function is being mocked. 
# How do I mock the other client.graphql and the client.create_flow function?

import unittest
import pytest
from pytest_mock import mocker
from unittest.mock import Mock
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

class test_flow(unittest.TestCase):
 @patch("code_file.client.graphql", side_effect=["test1s", "test2s"])
 @patch("code_file.client.create_flow", return_value="test_id")
 def test_flow(self, s1):
   # Below post call invokes run_flow function
   response = client.post("/name1/name2")
   assert run_flow("name1", "name2") == "test_id"

First mock call to graphql is succeeding. The second graphql call is not getting mocked. It is trying to contact the actual server and receiving 404. How can I mock both graphql client calls?

Advertisement

Answer

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"])
    ...
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement