【问题标题】:How to spec a Python mock which defaults to AttributeError() without explicit assignment?如何指定默认为 AttributeError() 而没有显式分配的 Python 模拟?
【发布时间】:2022-01-10 12:00:24
【问题描述】:

我有一个与 Python unittest.mock.Mockspec_set 功能相关的问题。

我的目标是创建一个具有以下功能的 Mock:

  1. 它有一个我在创建时决定的任意类的规范。
  2. 我必须能够根据第 1 点的规范仅分配模拟属性或方法
  3. Mock 在以下情况下必须引发 AttributeError:
    • 我尝试分配一个不在规范中的属性
    • 我调用或检索spec_set 中缺少的属性,或spec_set 中存在但根据上述点分配的属性。

我想要的一些行为示例:

class MyClass:
   property: int = 5
   def func() -> int:
       pass

# MySpecialMock is the Mock with the functionalities I am dreaming about :D
mock = MyMySpecialMock(spec_set=MyClass)

mock.not_existing # Raise AttributeError
mock.func() # Raise AttributeError
mock.func = lambda: "it works"
mock.func() # Returns "it works"

我尝试了多种解决方案,但没有任何运气,或者没有明确冗长。以下是一些例子:

  1. 使用Mock(spec_set=...),但如果我调用未明确设置的特定属性,它不会引发错误
  2. 使用Mock(spec_set=...) 并使用具有异常副作用的函数显式覆盖每个属性,但这非常冗长,因为我必须重复所有属性...

我的目标是找到一种自动化 2 的方法,但我没有干净的方法来做到这一点。你有遇到过这样的问题,并解决了吗?

对于好奇的人来说,目标是能够增强单元测试的分离性;我想确保只在我明确设置的方法上调用我的模拟,以避免奇怪和意外的副作用。

提前谢谢你!

【问题讨论】:

    标签: python unit-testing mocking python-unittest


    【解决方案1】:

    我想我通过使用dir 方法找到了一个令人满意的答案。

    要创建符合我上面列出的要求的 Mock,执行以下操作就足够了:

    def create_mock(spec: Any) -> Mock:
        mock = Mock(spec_set=spec)
    
        attributes_to_override = dir(spec)
        for attr in filter(lambda name: not name.startswith("__"), attributes_to_override):
            setattr(mock, attr, Mock(side_effect=AttributeError(f"{attr} not implemented")))
    
        return mock
    

    【讨论】:

      【解决方案2】:

      spec_set 定义了一个与类相同的mock 对象,但不允许对其进行任何更改,因为它定义了特殊的__getattr____setattr__。这意味着第一个测试(调用不存在的 attr)将按预期失败,但随后尝试设置 attr 也会失败:

      from unitest import mock
      
      class X:
          pass
      
      m = mock.Mock(spec_set=X)
      m.func()
      # __getattr__: AttributeError: Mock object has no attribute 'func'
      m.func = lambda: "it works"
      # __setattr__: AttributeError: Mock object has no attribute 'func'
      

      相反,您可以使用 create_autospec() 复制现有函数,并向其中添加 mock 函数,但不会影响 __setattr__

      n = mock.create_autospec(X)
      n.func()
      # __getattr__: AttributeError: Mock object has no attribute 'func'
      n.func = lambda: "it works"
      n.func()
      # 'it works'
      

      【讨论】:

      • 感谢您的回答!恐怕这会产生与我所描述的行为略有不同的行为;我的目标是让该类X 像:``` class X: def func(): pass ``` 具有以下行为:``` mock_x.func() # getattr : AttributeError: Mock object has no attribute 'func' n.func = lambda: "it works" n.func() # 'it works' ``` 我的意思是func确实应该是规范的一个属性,但getattr 必须在调用setattr 之后才能工作。
      猜你喜欢
      • 2014-07-17
      • 1970-01-01
      • 1970-01-01
      • 2010-10-31
      • 1970-01-01
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      相关资源
      最近更新 更多