调用两次时如何模拟具有相同名称的函数?

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

如何模拟在同一文件中使用不同参数调用两次的函数? 这是我的代码片段:

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"

对 graphql 的第一次模拟调用成功。第二个 graphql 调用没有被嘲笑。它正在尝试联系实际服务器并收到 404。 我如何模拟两个 graphql 客户端调用?

我试图重现你的代码,但对我来说 client.graphql 被完美地模拟了。

这是我的代码。

文件夹结构

├── 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})
            ]
        )

运行 使用命令进行单元测试

python -m unittest code_test.py

产生以下输出

First client call return value:-  {'data': [{'id': 1}]}
Second client call return value:-  {'data': [{'id': 2}]}
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

您会看到 prefect.Client 方法中的打印没有打印出来。

原回答

说明

来自python官方文档

If side_effect is an iterable then each call to the mock will return the next value from the iterable.

当您使用可迭代对象设置 side_effect 参数时,模拟将从可迭代对象中 return 下一个值。可以找到更多详细信息 here

解决方案

def test_flow(mocker):
    # Some lines of code, not shown here
    m = mocker.patch("code_file.client.graphql", side_effect=["value1", "value2"])
    ...