In my attempt to learn TDD, trying to learn unit testing and using mock with python. Slowly getting the hang of it, but unsure if I'm doing this correctly. Forewarned: I'm stucking using python 2.4 because the vendor API's come as pre-compiled 2.4 pyc files, so I'm using mock 0.8.0 and unittest ( not unittest2 )
Given this example code in 'mymodule.py'
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
Now in my test case file 'test_myclass.py', I want to mock the ldap object out. ldap.initialize returns the ldap.ldapobject.SimpleLDAPObject, so I figured that'd be the method I'd have to mock out.
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
Leads me to a couple of questions:
Thanks.
Just assign the exception to side_effect instead: mockedObj. raiseError. side_effect = Exception("Test") . You don't have to no: and his edit has a third way of doing it where he is making a Mock with the side effect set, but its still a valid, and good thing to know how to do in testing.
mock.
You can use patch()
as a class decorator, not just as a function decorator. You can then pass in the mocked function as before:
@patch('mymodule.SomeClass') class MyTest(TestCase): def test_one(self, MockSomeClass): self.assertIs(mymodule.SomeClass, MockSomeClass)
See: Applying the same patch to every test method (which also lists alternatives)
It makes more sense to set up the patcher this way on setUp if you want the patching to be done for all the test methods.
I'll start by answering your questions, and then I'll give a detailed example of how patch()
and setUp()
interact.
@patch()
decorator on setUp()
. You got lucky, because the object is created in setUp()
and never gets created during the test method.patch.object()
here. It just lets you patch attributes of an object instead of specifying the target as a string.To expand on my answer to question #3, the problem is that the patch()
decorator only applies while the decorated function is running. As soon as setUp()
returns, the patch is removed. In your case, that works, but I bet it would confuse someone looking at this test. If you really only want the patch to happen during setUp()
, I would suggest using the with
statement to make it obvious that the patch is going to be removed.
The following example has two test cases. TestPatchAsDecorator
shows that decorating the class will apply the patch during the test method, but not during setUp()
. TestPatchInSetUp
shows how you can apply the patch so that it's in place during both setUp()
and the test method. Calling self.addCleanUp()
makes sure that the patch will be removed during tearDown()
.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With