有没有办法将一个被mock的方法重置为其原始状态? - Python Mock - mock 1.0b1

54

我有以下简化的类需要进行mock:

class myClass(object):
    @staticmethod
    def A():
        #...

    def check(self):
        #code...
        value = self.A()
        #more code...

在我的第一个测试中,我仅模拟了方法A。

from django.test import TestCase
from mock import MagicMock
import myClass

class FirstTest(TestCase):

def setUp(self):
    myClass.A = MagicMock(return_value = 'CPU')

def test(self):
    #some tests 
    myClassObj = myClass()
    myClassObj.check()

而在我的第二个测试中,我模拟了整个检查方法:

from django.test import TestCase
from mock import MagicMock
import myClass

class SecondTest(TestCase):

def setUp(self):
    myClass.check = MagicMock(return_value = someObject)

def test(self):
    #some tests 
    myClassObj = myClass()
    myClassObj.check()

现在我的第一个测试中的断言失败了,原因是它调用的不是在check()内部调用A()并进行模拟的check(),而是在第二个测试中完全模拟的check()

有没有办法在测试后清除并将该方法设置为“正常”状态?我已经尝试过myClass.check.reset_mock(),但它似乎没有做任何事情。改变测试顺序也没有任何效果。

我正在使用来自http://pypi.python.org/pypi/mock/的Python Mock 1.0b1。


嗨,Dana,你能否发布一下(可能是简化过的)由“#some tests”表示的代码?这会让我更清楚地了解你想要实现什么。 - aychedee
3个回答

59
你可以使用mock.patch作为装饰器或上下文管理器:
from mock import patch, MagicMock

@patch('myClass.A', MagicMock(return_value='CPU'))
def test(self):
    pass
或:
def test(self):
    with patch('myClass.A', MagicMock(return_value='CPU')):
        pass

如果你没有向patch提供模拟对象,那么它会提供一个自动模拟的模拟对象,你可以修改它:

@patch('myClass.A')
def test(self, mock_A):
    mock_A.return_value = 'CPU'
    pass

或:

def test(self):
    with patch('myClass.A') as mock_A:
        mock_A.return_value = 'CPU'
        pass

在所有情况下,当装饰的测试函数或上下文管理器执行结束时,原始值将被恢复。


2
很棒,修补程序装饰器非常有用。 - Richard Knop
2
@SaiyanGirl:虽然我理解你想要补偿提供的努力,但答案应该因为它们本身是正确的而被点赞,而不是因为作者在另一个问题上回答正确。 - ereOn

21

您可以将函数放在self上并在完成后将其恢复。

import unittest

from mock import MagicMock
from MyClass import MyClass

class FirstTest(unittest.TestCase):

    def setUp(self):
        self.A = MyClass.A
        MyClass.A = MagicMock(name='mocked A', return_value='CPU')


    def tearDown(self):
        MyClass.A = self.A

    def test_mocked_static_method(self):
        print 'First Test'
        print MyClass.check
        print MyClass.A


class SecondTest(unittest.TestCase):

    def setUp(self):
        MyClass.check = MagicMock(name='mocked check', return_value=object)

    def test_check_mocked_check_method(self):
        print 'Second Test'
        print MyClass.check
        print MyClass.A


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

运行此文件将产生以下输出:

First Test
<unbound method MyClass.check> 
<MagicMock name='mocked A' id='141382732'>
Second Test
<MagicMock name='mocked check' id='141382860'>
<unbound method MyClass.A>

我现在发现自己比以前更多地使用patch装饰器,而不是setUp和tearDown。在这种情况下,你可以这样做:

from mock import patch

@patch('MyClass.A')
def test_mocked_static_method(self, mocked_A)
    mocked_A.return_value = 'CPU'
    # This mock will expire when the test method is finished

我喜欢这个模式。根据文档https://docs.python.org/3.5/library/unittest.mock-examples.html#applying-the-same-patch-to-every-test-method,对于测试类中的所有测试方法进行Mocking可能会变得混乱,需要使用`@patch`。 - Aaron Lelevier

0

我经常遇到相同的问题,为了解决它,我会做这些事情:

import sys
from django.test import TestCase
from mock import MagicMock
import myClass

class FirstTest(TestCase):

@classmethod
def setUpClass(cls):
    global myClass
    del sys.modules[myClass.__module__]
    import myClass

def setUp(self):
    myClass.A = MagicMock(return_value = 'CPU')

def test(self):
    #some tests 
    myClassObj = myClass()
    myClassObj.check()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接