python模拟不同的实例

时间:2018-06-05 16:04:19

标签: python mocking python-mock

我想知道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())正在调用该函数。

现在,我如何设置execute方法的return_value?

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

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

1 个答案:

答案 0 :(得分:0)

Python非常注重文字。表达式pg.connect()转换为:

  1. 查找名称pg
  2. 在其上查找属性connect
  3. 用空参数调用它。
  4. 因此表达式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____call__以及__eq__ dunder方法。虽然对象具有提供属性的常规方式,但__getattribute__允许类在请求属性时执行任何操作。

    当您将其称为Mock(spec=psycopg2)时,它将以与Mock()不同的方式执行两项主要操作:

    1. 调整其__class__字段以声明为psycopg2
    2. 检查psycopg2是否有效属性,如果您尝试获取不存在的字段,请引发AttributeError
    3. 但除此之外,它仍然表现为常规模拟。如果您致电yourMock.foo,则不知道它是属性还是方法。它只返回一个新的Mock实例,因为该实例是可调用的,所以你可以像函数一样调用它。

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

      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方法。