简体   繁体   English

模拟带有可选参数的函数

[英]mock on function with optional arguments

I want to write a test for "A" function which calls another "B" function in it.我想为“A”函数编写一个测试,它调用其中的另一个“B”函数。 B function looks like this: B 函数如下所示:

def Bfuncion(self, city: Optional[str], name: Optional[str]):

in A function I use B function two times, first time I use it in this way:在 A 函数中我使用了 B 函数两次,第一次是这样使用的:

cities = self.Bfunction(name=None, city="ny", page=page)

and the second time in this way:第二次以这种方式:

cities = self.Bfunction(name="Tom", city=None, page=page)

Now I want to write a test of A function, I would try this if there was only one use of B function:现在我想写一个 A 函数的测试,如果 B 函数只有一个用途,我会试试这个:

mocker.patch(
        "Path.To.My.Function",
        return_value=[
            {"someReturnValue"},
            {"someReturnValue"},
            {"someReturnValue"},
        ],

How do I write call mocker() , as I use B function with different arguments in each call.我如何编写 call mocker() ,因为我在每次调用中使用具有不同参数的 B 函数。

I try to suggest you a solution for your test;我尝试为您的测试建议一个解决方案; I don't know if it is suited for your need because I have tried to guess some details that you have omitted in your question.我不知道它是否适合您的需要,因为我试图猜测您在问题中遗漏的一些细节。

The production code生产代码

I suppose that your production code is contained in a file called functions.py and the content of functions.py is the following:我想您的生产代码包含在一个名为functions.py的文件中, functions.py的内容如下:

from  typing import Optional

class MyClass:

    def Bfunction(self, city: Optional[str], name: Optional[str]):
        return city

    def Afunction(self):
        cities = self.Bfunction(name=None, city="ny", page='page')
        print(cities)
        cities = self.Bfunction(name="Tom", city=None, page='page')
        print(cities)

Note that in functions.py :请注意,在functions.py中:

  1. I have defined a class because I have seen that your definition of the Bfunction contains the self argument.我定义了一个类,因为我看到您对Bfunction的定义包含self参数。
  2. The Afunction executes the two calls of Bfunction that you have write in your question. Afunction执行您在问题中编写的Bfunction的两个调用。

The test file测试文件

The test file (which doesn't use pytest but only the module unittest ) is the following:测试文件(不使用pytest而只使用模块unittest )如下:

import unittest
from unittest import mock
import functions

class MyTestCase(unittest.TestCase):

    def test_something(self):
        sut = functions.MyClass()
        with mock.patch.object(sut, 'Bfunction') as mock_b:
            # set values returned by Bfunction in the first and in the second call
            mock_b.side_effect = ['cityA', 'cityB']
            sut.Afunction()
            # here I verify that Bfunction is called 2 times
            self.assertEqual(2, mock_b.call_count)
            mock_b.assert_called_with(city=None, name='Tom', page='page')
            mock_b.assert_called_with(name="Tom", city=None, page='page')


if __name__ == '__main__':
    unittest.main()

The output of test file execution测试文件执行的输出

The output of the test file execution is:测试文件执行的输出是:

cityA
cityB
.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

The output showes:输出显示:

  1. Afunction calls exactly two times the Bfunction Afunction调用Bfunction的两倍
  2. the test verify the arguments passed to Bfunction测试验证传递给Bfunction的参数
  3. the presence of cityA and cityB in the output show you how to select different values returned by Bfunction (you have to use side_effect and not return_value ).输出中cityAcityB的存在向您展示了如何选择Bfunction返回的不同值(您必须使用side_effect而不是return_value )。

I hope that this answer is useful for you.我希望这个答案对你有用。

Use the side_effect keyword argument, not the return_value keyword argument.使用side_effect关键字参数,而不是return_value关键字参数。

>>> from unittest.mock import Mock
>>> m = Mock(return_value=[1,2,3])
>>> m()
[1, 2, 3]
>>> m = Mock(side_effect=[1,2,3])
>>> m()
1
>>> m()
2
>>> m()
3

( patch , of course, simply passes keyword arguments it does not itself recognize to Mock to configure the object that is used by the patched name.) (当然, patch只是将它本身无法识别的关键字参数传递给Mock以配置被修补名称使用的对象。)

This requires you to know ahead of time the order of calls that will be made.这要求您提前知道将要进行的呼叫的顺序。 If you need more flexibility, patch the name with a custom function that behaves the way you want, instead of a Mock object.如果您需要更大的灵活性,请使用按您想要的方式运行的自定义函数而不是Mock对象来修补名称。

def my_func(self, name, city, page):
    ...
mocker.patch(
    "Path.To.My.Function",
    new=my_func
)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM