如何断言接受Python模拟序列参数的调用?

如何断言接受Python模拟序列参数的调用?,python,unit-testing,mocking,Python,Unit Testing,Mocking,我正在处理一系列用户定义的对象。它看起来类似于以下内容: class Thing(object): def __init__(self, x, y): self.x = x self.y = y def my_function(things): x_calc = calculate_something(t.x for t in things) y_calc = calculate_something(t.y for t in things

我正在处理一系列用户定义的对象。它看起来类似于以下内容:

class Thing(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
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
我目前正在测试的方法具有与以下类似的功能:

class Thing(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
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
我面临的问题是测试对
calculate\u something
的调用。我想断言这些调用发生过,类似这样:

calculateSomethingMock.assert_any_call(the_sequence)
我不关心传递到
calculate\u something
的序列的顺序,但我关心元素是否都存在。我可以在调用
set
的过程中封装生成器函数,但我觉得我的测试不应该记录传递给
计算对象的序列类型。我应该可以通过任何序列。我也可以创建一个生成序列的方法,而不是使用生成器语法并模拟该方法,但这似乎有些过分

我如何才能最好地构造这个断言,或者我在这里测试时遇到的麻烦是否表明代码结构不良

我正在使用Python2.7.3和Mock 1.0.1

(对于那些觉得有必要对此发表评论的人,我知道我在做最后一次测试,这并不是最好的做法。)

编辑:


看完之后,我重新考虑了是否应该模拟
计算某物
方法。

查看模拟文档,有一种方法可以满足您的需要

因此,您将在测试中模拟出
计算内容

calculate_something = Mock(return_value=None)
my_函数
完成后,您可以通过执行以下操作来检查传递的参数:

calculate_something.call_args_list
它将返回对它进行的所有调用的列表(传递了相应的元素)

编辑:

(很抱歉花了我这么长时间,我不得不在我的机器上安装Python3.3)

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
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()
测试文件.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
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()

我已经有很长一段时间没有接触到我最初使用的代码了,但是我已经重新考虑了我的测试方法。我一直在努力对我所做的事情更加小心,不要嘲笑。我最近意识到,我正在无意识地开始遵循这条经验法则:如果它使我的测试更短、更简单,就模仿它;如果它使测试更复杂,就别管它。对于这种方法,简单的输入/输出测试就足够了。没有像数据库或文件这样的外部依赖项。所以简而言之,我想我的问题的答案是,“我不应该模仿
计算某物”
,这样做会使我的测试更难阅读和维护。

我尽量避免深入查看该列表,但我想这可能是唯一的方法。你能举个例子吗?谢谢。这个测试依赖于保持顺序的方法,也依赖于传递列表的方法。我不太喜欢依赖订单的测试。如果我决定将传递到
calculate\u something
的参数更改为其他数据结构,是否希望测试失败?或者相反,如果我更改测试以检查不同数据类型的调用,是否需要修改该方法以使其再次通过?是的,但请记住这是一个示例。您可以通过
calculate\u something
任何其他数据结构,只需更新测试以反映这一点(通过更改预期值)。毕竟你希望考试通过。您已经看到了如何使用
call\u args\u list
将参数传递给
calculate\u something
。之后,只需将传递的结构化数据与预期数据进行比较。