首页 > 解决方案 > python模拟不同的实例

问题描述

我想知道pg.connectpg.connect()声明之间的区别。

pg是一个 psycopg2 模拟对象,当我显示 时dir,它显示 connect 作为它的方法。

class TestMock(unittest.TestCase):
    def setUp(self):
        pass

    def test_mock_calls(self):
        import pprint
        pg = mock.MagicMock(psycopg2)
        print 'pg() object ', pg()
        print 'dir of pg = ', pprint.pprint(dir(pg))

        print 'dir of pg = ', pg.connect
        print 'dir of pg.connect = ', pprint.pprint(dir(pg.connect))

        print 'dir of pg = ', pg.connect()
        print 'dir of pg.connect() = ', 
        pprint.pprint(dir(pg.connect()))

更新:-一个(pg.connect)正在访问该属性,另一个(pg.connect())正在调用该函数。

现在,如何设置执行方法的 return_value?

例如:- 我需要模拟我的执行方法的响应。

所以,它应该是 pg.connect().cursor().execute.side_effect = someexception()

标签: pythonmockingpython-mock

解决方案


Python 的思想非常简单。该表达式pg.connect()转换为:

  1. 查名字pg
  2. 查找它的属性connect
  3. 用空参数调用它。

所以一个表达式pg.connect将获得方法,但不会调用它。而且,事实上,您可以将该方法用作常规值并将其传递,因此my_connect = pg.connect完全有效,您可以稍后调用my_connect().

我们可以看到dis模块发生了什么:

import dis
dis.dis(lambda: pg.connect())

 1           0 LOAD_GLOBAL              0 (pg)
             2 LOAD_ATTR                1 (connect)
             4 CALL_FUNCTION            0
             6 RETURN_VALUE

由于Mock要伪装成不同的对象,所以它覆盖了标准的__getattribute__and__call____eq__dunder 方法。虽然对象具有提供属性的正常方式,__getattribute__但如果请求属性,则让类做任何它想做的事情。

当您将其称为Mock(spec=psycopg2)时,它将做两件与 不同的主要事情Mock()

  1. 它调整其__class__字段以声称是psycopg2
  2. 检查 psycopg2 的有效属性,AttributeError如果您尝试获取不存在的字段,请提出。

但除此之外,它仍然表现得像一个普通的模拟。如果您调用yourMock.foo,它不知道它是属性还是方法。它只是返回一个新Mock实例,并且因为该实例是可调用的,所以您可以像调用函数一样调用它。

更新:如果你设置 spec on Mock,它会限制可用的属性。要添加规范未提供的属性,您只需要在初始化时设置它:

cursor_mock = Mock(side_effect=psycopg2.DatabaseError)
Mock(spec=psycopg2.connect, cursor=cursor_mock)

你也想要这种行为:pg.connect().cursor().execute.side_effect = someexception(). 一般来说,你会改变return_value

cursor_mock = Mock()
cursor_mock.return_value.execute.side_effect = SomeException
# Same as:
cursor_mock = Mock(return_value=Mock(execute=Mock(side_effect=SomeException)))

你也可以这样做cursor_mock().execute.side_effect = SomeException,但是那样会污染cursor_mock.mock_calls

小点:Mock为了简洁,我用的和MagicMock是一样的,只是加了一些dunder的方法。


推荐阅读