Pages

Monday, 25 April 2011

Calling only once setUp in unittest in python

In python unittest the setUp function gets called every time you need to run a single TestCase (no matter if you run it using TestSuite or simply unittest.main()).

So if you have to test something that requires a complex setUp routine (like loading 50000 records from SQL or preparing an orangemochafrappuccino) but that can be reused all along your TestSuite you have the same problem I had.

Now all around the internet there are posts saying "You could do that" or "One way to do that" or "Once I did that like this..." or even worse "The standard way of doing it is...".

But none of these posts really gets to the bottom of it, they all give you a piece of information. I couldn't find one place that told you "copy/paste this code and be happy".

So, after figuring it out, I can now tell you "copy/paste this code and be happy".
import unittest
from my_program import MyClass

class MyClassTest(unittest.TestCase):
    # First define a class variable that determines 
    # if setUp was ever run
    ClassIsSetup = False

    def setUp(self):
        # If it was not setup yet, do it
        if not self.ClassIsSetup:
            print "Initializing testing environment"
            # run the real setup
            self.setupClass()
            # remember that it was setup already
            self.__class__.ClassIsSetup = True
                               
    def setupClass(self):
        # Do the real setup
        unittest.TestCase.setUp(self)
        # you want to have persistent things to test
        self.__class__.myclass = MyClass()
        # (you can call this later with self.myclass)

You can do the same for the unittest.tearDown since it is exactly the same code.

Mocking LDAP calls with minimock in python

When using unittest in python you often need to mock objects and calls to those objects.

You don't need _an_entire_ LDAP server just to test a search nor you need a full blown SMTP server to test a function that accidentally sends a mail (that you don't want to send while testing anyway).

So minimock is one easy way (the easiest I found) to do the job.

What the docs don't really tell you (nor does a google search) is how to return real things when you need to call a method of an object.

Say you have some code like this:
import ldap
class MyClass:
...
    def ldapsearch(self, uid):
        self.conn = ldap.open(self.conf.ldapserver)
        self.conn.bind_s(binddn, bindpw, ldap.AUTH_SIMPLE)
        self.conn.search_s(basedn, ldap.SCOPE_SUBTREE, 'uid=%s' % uid, ['uid'])
        return True

and you want to test this and moreover you want that search to return a given set of data.

from minimock import Mock
import ldap
from my_program import MyClass

class MyClassTest(unittest.TestCase):
...
    def test_ldapsearch(self):
        # create your fake ldap connection
        ldap.open = Mock('ldap.open')
        ldap.open.mock_returns = Mock('ldap_connection')
        # instantiate your class
        myclass = MyClass()
        # now tell to minimock that in case we do a search_s on 
        # a conn method this should be intercepted and mocked
        # returning a testuser1 value the way LDAP would do
        myclass.conn.search_s.mock_returns = [[1,{'uid':['testuser1'], \
                                        'mail':['test.user1@mail.com']}]]
        # This will return testuser1 no matter what
        # and show you on screen the operations performed
        self.assertEqual(myclass.ldapsearch('onetwoonetwo'), True)

This should help you inject whatever values you want there and also clarify a bit how to minimock wants you to think (that is the only way I could make it work).