【问题标题】:How do I mock a class and control a returned value in py.test with pytest-mock?如何使用 pytest-mock 模拟一个类并控制 py.test 中的返回值?
【发布时间】:2016-12-19 05:20:50
【问题描述】:

我正在尝试使用py.test 来测试一些执行各种LDAP 搜索和修改的代码。

我正在使用 pytest-mock,但我无法理解如何模拟 LDAP 对象的创建,以及在模拟对象上调用 search_s() 时控制它返回的内容。

我以为这会做我想要的,但测试失败了,计数显示生成器函数 find_users() 永远不会产生任何结果。

import pytest
# Here is some code to simply test mocking out ldap.initialize(), and
# controlling the return value from calls to search_s()
import ldap

def find_users(ldap_url, admin_user, admin_password, userbase):
    lobj = ldap.initialize(ldap_url)
    lobj.simple_bind_s(admin_user, admin_password)
    for i in lobj.search_s(userbase, ldap.SCOPE_SUBTREE, '*'):
        yield i[1]['uid'][0]

class TestMocking:

    @pytest.fixture()
    def no_ldap(self, mocker):
        return mocker.patch('ldap.initialize')


    def test_ad_one_user(self, no_ldap):
        # try and modify how search_s() would return
        no_ldap.search_s.return_value = ('', {'uid': ['happy times']})
        count = 0 
        for i in find_users('', '', '', ''):
            count += 1
            assert i=='happy times'
        assert count == 1

【问题讨论】:

    标签: python mocking pytest


    【解决方案1】:

    您可以直接使用补丁(并且您的结构有些问题):

    from mock import patch, Mock
    import pytest
    # Here is some code to simply test mocking out ldap.initialize(), and
    # controlling the return value from calls to search_s()
    import ldap
    
    def find_users(ldap_url, admin_user, admin_password, userbase):
        lobj = ldap.initialize(ldap_url)
        lobj.simple_bind_s(admin_user, admin_password)
        for i in lobj.search_s(userbase, ldap.SCOPE_SUBTREE, '*'):
            yield i[1]['uid'][0]
    
    class TestMocking:
    
        @patch('ldap.initialize')
        def test_ad_one_user(self, no_ldap):
            # try and modify how search_s() would return
            data = [('', {'uid': ['happy times']})]
            search_s = Mock(return_value=data)
            no_ldap.return_value = Mock(search_s=search_s)
            count = 0
            for i in find_users('', '', '', ''):
                count += 1
                assert i=='happy times'
            assert count == 1
    

    【讨论】:

    • 感谢@salparadise 的回答。这个可行,但我仍然感到困惑,因为我认为 py.test “方式”是使用固定装置,因此您可以在测试中重复使用它们。
    • @GeoffCrompton 取决于您的目标。 fixtures 允许对每个模块或测试会话进行范围界定,并且默认情况下仅在一个函数上进行(就像这样做一样),但是您也可以使用 unittest.TestCase 并使用setUp 函数来为所有单元测试初始化​​补丁类。
    • You can just use patch directly - 这在某处有记录吗?
    • @dm295 这就是你想要的docs.python.org/3/library/…
    • @salparadise 谢谢。我知道如何使用patchunittests。但我不知道pytest 也可以使用。
    【解决方案2】:

    我认为您可能对 https://docs.python.org/3/library/unittest.mock.html 和 pytest 猴子补丁感到困惑。我认为两者的行为方式不同。

    您可以使用模拟补丁使其工作 (https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch)

    @pytest.fixture()
    def no_ldap(self):
        patcher = mock.patch('ldap.initialize')
        patcher.start()
        yield patcher
        patcher.stop()
      
    

    【讨论】:

    • 不错。另请参阅this answer,了解使用 patch() 作为上下文管理器来做同样事情的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多