简体   繁体   English

如何使用Python Mock断言接受序列参数的调用?

[英]How can I assert calls that accept sequence arguments with Python Mock?

I am processing a sequence of user-defined objects. 我正在处理一系列用户定义的对象。 It looks similar to the following: 它看起来类似于以下内容:

class Thing(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

The method I am currently testing has functionality similar to the following: 我目前正在测试的方法具有类似于以下的功能:

def my_function(things):
    x_calc = calculate_something(t.x for t in things)
    y_calc = calculate_something(t.y for t in things)
    return x_calc / y_calc

The problem I am facing is testing the calls to calculate_something . 我面临的问题是测试对calculate_something的调用。 I want to assert that these calls happened, something like so: 我想断言这些调用发生了,就像这样:

calculateSomethingMock.assert_any_call(the_sequence)

I don't care about the order of the sequence passed into calculate_something , but I do care that the elements are all present. 我不关心传递给calculate_something的序列的顺序,但我确实关心元素都存在。 I could wrap the generator function in a call to set , but I don't feel like my test should be dictating what type of sequence is passed into calculate_something . 我可以在调用set包装生成器函数,但我不觉得我的测试应该指示将什么类型的序列传递给calculate_something I should be able to pass it any kind of sequence. 我应该能够以任何顺序传递它。 I could alternatively create a method that generates the sequence instead of using generator syntax and mock that method, but that seems like overkill. 我也可以创建一个生成序列的方法,而不是使用生成器语法并模拟该方法,但这似乎有点过分。

How can I best structure this assertion, or is my trouble testing here an indication of poorly structured code? 我怎样才能最好地构建这个断言,或者我在这里测试是否存在结构不良的代码?

I am using Python 2.7.3 with Mock 1.0.1. 我使用Python 2.7.3与Mock 1.0.1。

(For anyone who feels compelled to comment on it, I'm aware I'm doing test last and that this isn't considered the greatest practice.) (对于那些觉得有必要发表评论的人,我知道我最后一次做测试,这不是最好的做法。)

Edit: 编辑:

After watch this marvelous talk entitled "Why You Don't Get Mock Objects by Gregory Moeck" , I have reconsidered whether I should even be mocking the calculate_something method. 在观看了这个名为“为什么你没有得到格雷戈里·莫克的模拟对象”的精彩演讲后,我重新考虑过我是否应该嘲笑calculate_something方法。

Looking at the Mock documentation, there is a call_args_list that will do what you want. 查看Mock文档,有一个call_args_list可以执行您想要的操作。

So you will Mock out calculate_something on your test. 所以你会在你的测试中模拟出calculate_something

calculate_something = Mock(return_value=None)

After you my_function has finished you can check the arguments passed by doing: my_function完成后,您可以通过执行以下操作检查传递的参数:

calculate_something.call_args_list

which will return a list of all the calls made to it (with the corresponding elements passed). 这将返回所有调用的列表(传递相应的元素)。

Edit : 编辑

(Sorry it took me so long, I had to install Python3.3 on my machine) (对不起,我花了这么长时间,我不得不在我的机器上安装Python3.3)

mymodule.py mymodule.py

class Thing:
    ...
def calculate_something:
    ...

def my_function(things):
    # Create the list outside in order to avoid a generator object
    # from being passed to the Mock object.

    xs = [t.x for t in things]
    x_calc = calculate_something(xs)

    ys = [t.y for t in things]
    y_calc = calculate_something(ys)
    return True

test_file.py test_file.py

import unittest
from unittest.mock import patch, call
import mymodule



class TestFoo(unittest.TestCase):

    # You can patch calculate_something here or
    # do so inside the test body with 
    # mymodule.calcualte_something = Mock()
    @patch('mymodule.calculate_something')
    def test_mock(self, mock_calculate):

        things = [mymodule.Thing(3, 4), mymodule.Thing(7, 8)]

        mymodule.my_function(things)

        # call_args_list returns [call([3, 7]), call([4, 8])]
        callresult = mock_calculate.call_args_list


        # Create our own call() objects to compare against
        xargs = call([3, 7])
        yargs = call([4, 8])

        self.assertEqual(callresult, [xargs, yargs])

        # or
        # we can extract the call() info
        # http://www.voidspace.org.uk/python/mock/helpers.html#mock.call.call_list
        xargs, _ = callresult[0]
        yargs, _ = callresult[1]

        xexpected = [3, 7]
        yexpected = [4, 8]

        self.assertEqual(xargs[0], xexpected)
        self.assertEqual(yargs[0], yexpected)

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

I haven't touched the code I was originally working with for a good while, but I've been reconsidering my approach to testing in general. 我没有触及我最初使用的代码,但我一直在重新考虑我的测试方法。 I've been trying to be more careful about what I do and don't mock. 我一直在努力更加小心我做什么,不要嘲笑。 I recently realized that I was unconsciously starting to following this rule of thumb: mock something if it makes my test shorter and simpler and leave it alone if it makes the test more complicated. 我最近意识到我无意识地开始遵循这个经验法则:如果它使我的测试变得更短更简单,那就嘲笑它,如果它使测试变得更复杂则不管它。 Simple input/output testing suffices in the case of this method. 在这种方法的情况下,简单的输入/输出测试就足够了。 There are no external dependencies like a database or files. 没有外部依赖项,如数据库或文件。 So in short, I think the answer to my question is, "I shouldn't mock calculate_something ." 所以简而言之,我认为我的问题的答案是,“我不应该模拟calculate_something 。” Doing so makes my test harder to read and maintain. 这样做会使我的测试更难以阅读和维护。

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

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