How to properly use mock in python with unittest setUp
在我尝试学习TDD时,尝试学习单元测试并使用mock与python。慢慢地掌握它,但不确定我是否正确地这样做。预警:我正在坚持使用python 2.4,因为供应商API是预先编译的2.4 pyc文件,所以我使用模拟0.8.0和unittest(不是unittest2)
给出'mymodule.py'中的示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import ldap class MyCustomException(Exception): pass class MyClass: def __init__(self, server, user, passwd): self.ldap = ldap.initialize(server) self.user = user self.passwd = passwd def connect(self): try: self.ldap.simple_bind_s(self.user, self.passwd) except ldap.INVALID_CREDENTIALS: # do some stuff raise MyCustomException |
现在在我的测试用例文件'test_myclass.py'中,我想模拟ldap对象。 ldap.initialize返回ldap.ldapobject.SimpleLDAPObject,所以我认为这是我必须嘲笑的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import unittest from ldap import INVALID_CREDENTIALS from mock import patch, MagicMock from mymodule import MyClass class LDAPConnTests(unittest.TestCase): @patch('ldap.initialize') def setUp(self, mock_obj): self.ldapserver = MyClass('myserver','myuser','mypass') self.mocked_inst = mock_obj.return_value def testRaisesMyCustomException(self): self.mocked_inst.simple_bind_s = MagicMock() # set our side effect to the ldap exception to raise self.mocked_inst.simple_bind_s.side_effect = INVALID_CREDENTIALS self.assertRaises(mymodule.MyCustomException, self.ldapserver.connect) def testMyNextTestCase(self): # blah blah |
引出了几个问题:
谢谢。
您可以使用
1 2 3 4 5 | @patch('mymodule.SomeClass') class MyTest(TestCase): def test_one(self, MockSomeClass): self.assertIs(mymodule.SomeClass, MockSomeClass) |
见:26.5.3.4。将相同的补丁应用于每种测试方法(也列出替代方案)
如果您希望对所有测试方法进行修补,那么在setUp上以这种方式设置修补程序更有意义。
我将首先回答您的问题,然后我将详细介绍
为了扩展我对问题#3的回答,问题是
以下示例有两个测试用例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import unittest from mock import patch @patch('__builtin__.sum', return_value=99) class TestPatchAsDecorator(unittest.TestCase): def setUp(self): s = sum([1, 2, 3]) self.assertEqual(6, s) def test_sum(self, mock_sum): s1 = sum([1, 2, 3]) mock_sum.return_value = 42 s2 = sum([1, 2, 3]) self.assertEqual(99, s1) self.assertEqual(42, s2) class TestPatchInSetUp(unittest.TestCase): def setUp(self): patcher = patch('__builtin__.sum', return_value=99) self.mock_sum = patcher.start() self.addCleanup(patcher.stop) s = sum([1, 2, 3]) self.assertEqual(99, s) def test_sum(self): s1 = sum([1, 2, 3]) self.mock_sum.return_value = 42 s2 = sum([1, 2, 3]) self.assertEqual(99, s1) self.assertEqual(42, s2) |
如果要应用许多补丁并且希望它们应用于setUp方法中初始化的内容,请尝试以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def setUp(self): self.patches = { "sut.BaseTestRunner._acquire_slot": mock.Mock(), "sut.GetResource": mock.Mock(spec=GetResource), "sut.models": mock.Mock(spec=models), "sut.DbApi": make_db_api_mock() } self.applied_patches = [mock.patch(patch, data) for patch, data in self.patches.items()] [patch.apply for patch in self.applied_patches] . . rest of setup . def tearDown(self): patch.stopall() |
我想指出一个接受的答案的变体,其中
1 2 3 4 5 6 7 8 | from unittest.mock import patch, Mock MockSomeClass = Mock() @patch('mymodule.SomeClass', new=MockSomeClass) class MyTest(TestCase): def test_one(self): # Do your test here |
请注意,在这种情况下,不再需要将第二个参数
可以在https://docs.python.org/3/library/unittest.mock.html#patch找到对此的解释:
If
patch() is used as a decorator and new is omitted, the created mock is passed in as an extra argument to the decorated function.
上面的答案都省略了新的,但包含它可能很方便。
您可以创建一个修补的内部函数并从
如果您的原始
1 2 | def setUp(self): some_work() |
然后你可以修改它来修改它:
1 2 3 4 5 6 | def setUp(self): @patch(...) def mocked_func(): some_work() mocked_func() |