如何在python中通过unittest设置正确使用mock
在我学习TDD的尝试中,我尝试学习单元测试和使用python模拟。慢慢掌握窍门,但不确定我是否做得对。预先警告:我正在使用python 2.4,因为供应商API是预编译的2.4 pyc文件,所以我使用的是模拟0.8.0和unittest(不是unittest2) 给出了“mymodule.py”中的示例代码如何在python中通过unittest设置正确使用mock,python,unit-testing,mocking,Python,Unit Testing,Mocking,在我学习TDD的尝试中,我尝试学习单元测试和使用python模拟。慢慢掌握窍门,但不确定我是否做得对。预先警告:我正在使用python 2.4,因为供应商API是预编译的2.4 pyc文件,所以我使用的是模拟0.8.0和unittest(不是unittest2) 给出了“mymodule.py”中的示例代码 import ldap class MyCustomException(Exception): pass class MyClass: def __init__(self
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对象。initialize返回ldap.ldapobject.SimpleLDAPObject,因此我认为这是我必须模拟的方法
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
让我想到几个问题:
@patch('mymodule.SomeClass')
class MyTest(TestCase):
def test_one(self, MockSomeClass):
self.assertIs(mymodule.SomeClass, MockSomeClass)
参见:(其中还列出了备选方案)
如果您希望对所有测试方法进行修补,则在设置中以这种方式设置修补程序更有意义。如果您有许多修补程序要应用,并且希望它们也应用于在设置方法中初始化的内容,请尝试以下方法:
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()
首先我将回答您的问题,然后我将给出一个关于
patch()
和setUp()
如何交互的详细示例
setUp()
上使用@patch()
装饰程序。您很幸运,因为对象是在setUp()
中创建的,在测试方法期间从未创建过patch.object()
。它只允许您修补对象的属性,而不是将目标指定为字符串patch()
decorator仅在修饰函数运行时应用。只要setUp()
返回,补丁就会被删除。在您的情况下,这是可行的,但我敢打赌,这会让查看此测试的人感到困惑。如果您真的只希望修补程序在setUp()
期间运行,我建议使用with
语句来明确说明修补程序将被删除
下面的示例有两个测试用例TestPatchAsDecorator
显示修饰类将在测试方法期间应用补丁,但在setUp()
期间不会应用补丁TestPatchInSetUp
显示了如何应用修补程序,以便在setUp()
和测试方法期间将其放置到位。调用self.addCleanUp()
可确保在tearDown()
期间删除修补程序
我想指出一个已接受答案的变体,其中一个
new
参数被传递给patch()
decorator:
from unittest.mock import patch, Mock
MockSomeClass = Mock()
@patch('mymodule.SomeClass', new=MockSomeClass)
class MyTest(TestCase):
def test_one(self):
# Do your test here
注意,在这种情况下,不再需要向每个测试方法添加第二个参数,MockSomeClass
,这可以节省大量代码重复
有关这方面的说明,请访问:
如果patch()
用作修饰符,并且省略了new,则创建的mock将作为额外参数传递给修饰函数
以上所有答案都省略了new,但包含它会很方便。您可以创建一个修补的内部函数,并从
设置中调用它
如果您原来的设置
功能是:
def setUp(self):
some_work()
然后,您可以将其更改为:
def setUp(self):
@patch(...)
def mocked_func():
some_work()
mocked_func()
1-3)我觉得很好。。。4)<代码>导入LDAP < /代码>,并设置<代码> SIDOSIFECTION=LDAP.ValuIDIORITION >您可以总是进行相同的测试,但用自己制作的更简单的对象……考虑使用<代码>修补程序.StudioAlpor()/<代码> <代码> TurDub()/代码>。考虑一个行:<代码>在Self.Apple的修补程序:Pux.Stad()/代码>它的代码> STOBALL ,而不是<代码> StutsOLL 。公平地说,我现在使用“Audio.AddiCuffUp(补丁)”方法。是时候更新这个答案了。self.addCleanup(patch.stopall)
我遇到了一个问题,我在TestCase类上有一个类级模拟,并假设在调用setUp()
方法时它已经存在。事实并非如此;类级模拟未及时应用于setUp()
中。相反,我通过创建我在所有测试中使用的helper方法解决了这个问题。我不确定这是最好的方法,但它是有效的。@berto如果你在回答中扩展你的评论,我认为这将是有帮助的。这是一个不同的解决方案,可能比这里的其他解决方案更简单。谢谢!这非常有帮助,尤其是当有许多模拟的函数类不需要特殊的返回值或类似值时。保持测试用例函数定义的干净。谢谢,这帮助我使我的代码更干净。我不需要在每个方法的顶部添加注释,因为我有很多模拟类。这是一个很好的例子,说明了为什么人们应该一直滚动到旧的StackOverflow问题上,以获得超出公认答案的更多见解!布拉沃:我想你应该给你提到的答案#3一个超链接,因为这样会根据他们得到的分数对答案进行排序。我明白你的意思,@ErdinEray,但我实际上是在说话
def setUp(self):
@patch(...)
def mocked_func():
some_work()
mocked_func()