【问题标题】:Nested spec_set in mock模拟中的嵌套 spec_set
【发布时间】:2017-06-27 06:55:40
【问题描述】:

我有以下两个文件:

testcase_module.py

import boto3


ec2 = boto3.resource('ec2')


def f():
    return ec2.instances.all()

testcase_test.py

import testcase_module
import unittest.mock


class MainTest(unittest.TestCase):
    @unittest.mock.patch('testcase_module.ec2', spec_set=['instances'])
    def test_f(self, ec2_mock):
        ec2_mock.instances.spec_set = ['all']
        testcase_module.f()


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

我在补丁中添加了spec_test 参数,因为我想断言是否调用了除instances.all() 之外的任何其他函数,但是将字符串'all' 更改为'allx' 不会在更改@987654327 时导致测试失败@ 到 'instancesx' 可以。我尝试了以下更改(git diff testcase_test.pypython testcase_test.py 结果如下):

尝试 1:

diff --git a/testcase_test.py b/testcase_test.py
index d6d6e59..ae274c8 100644
--- a/testcase_test.py
+++ b/testcase_test.py
@@ -3,9 +3,8 @@ import unittest.mock


 class MainTest(unittest.TestCase):
-    @unittest.mock.patch('testcase_module.ec2', spec_set=['instances'])
-    def test_f(self, ec2_mock):
-        ec2_mock.instances.spec_set = ['all']
+    @unittest.mock.patch('testcase_module.ec2', spec_set=['instances.all'])
+    def test_f(self, _):
         testcase_module.f()

生产:

E
======================================================================
ERROR: test_f (__main__.MainTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3.5/unittest/mock.py", line 1157, in patched
    return func(*args, **keywargs)
  File "testcase_test.py", line 8, in test_f
    testcase_module.f()
  File "/path/to/project/testcase_module.py", line 8, in f
    return ec2.instances.all()
  File "/usr/lib/python3.5/unittest/mock.py", line 578, in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'instances'

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

尝试 2:

diff --git a/testcase_test.py b/testcase_test.py
index d6d6e59..d93abd1 100644
--- a/testcase_test.py
+++ b/testcase_test.py
@@ -3,9 +3,8 @@ import unittest.mock


 class MainTest(unittest.TestCase):
-    @unittest.mock.patch('testcase_module.ec2', spec_set=['instances'])
-    def test_f(self, ec2_mock):
-        ec2_mock.instances.spec_set = ['all']
+    @unittest.mock.patch('testcase_module.ec2.instances', spec_set=['all'])
+    def test_f(self):
         testcase_module.f()

生产:

E
======================================================================
ERROR: test_f (__main__.MainTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3.5/unittest/mock.py", line 1149, in patched
    arg = patching.__enter__()
  File "/usr/lib/python3.5/unittest/mock.py", line 1312, in __enter__
    setattr(self.target, self.attribute, new_attr)
AttributeError: can't set attribute

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.5/unittest/mock.py", line 1170, in patched
    patching.__exit__(*exc_info)
  File "/usr/lib/python3.5/unittest/mock.py", line 1334, in __exit__
    delattr(self.target, self.attribute)
AttributeError: can't delete attribute

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

当调用了instances.all 以外的其他方法时,如何使其失败?

【问题讨论】:

    标签: python python-3.x python-unittest python-mock


    【解决方案1】:

    尝试使用mock_add_spec

    ec2_mock.instances.mock_add_spec(['all'], spec_set=True)

    链接:https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.mock_add_spec

    【讨论】:

      【解决方案2】:

      这样怎么样:

      @unittest.mock.patch('testcase_module.boto3.resource', autospec=True)
      def test_f(self, ec2_resource_mock):
          class InstanceStub(object):
              def all(self):
                  return [...]
          ec2_resource_mock.return_value = mock.create_autospec(
              EC2InstanceType, instances=InstanceStub())
          testcase_module.f()
      

      【讨论】:

      • 什么是EC2InstanceType
      • 为什么模拟 resource 函数而不是 ec2 对象如此重要?我在问,因为您不是第一个建议这种方法的人,但我仍然无法理解实践中的差异。嘲讽resource 好像有点复杂。
      • 这是最简单的方法,因为您真的对限制资源的返回值感兴趣。
      • 这不是“为什么”问题的答案。模拟 ec2 对象对我来说听起来更容易。为什么模拟资源对您来说更容易?
      • 因为ec2只是一个名字,引用调用的返回值才是你真正感兴趣的。
      猜你喜欢
      • 1970-01-01
      • 2016-05-10
      • 2016-04-02
      • 1970-01-01
      • 1970-01-01
      • 2011-07-18
      • 2016-11-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多