Writing a testable story with mockito and nose

When our code interacts with other classes through methods and attributes, these are referred to as collaborators. Mocking out collaborators using mockito (http://code.google.com/p/mockito and http://code.google.com/p/mockito-python) provides a key tool for BDD. Mocks provide a way for providing canned behavior compared to stubs, which provide canned state. While mocks by themselves don't define BDD, their usage keenly overlaps the ideas of BDD.

To further demonstrate the behavioral nature of the tests, we will also use the spec nose plugin found in the pinocchio project (http://darcs.idyll.org/~t/projects/pinocchio/doc).

Getting ready

For this recipe, we will be using the shopping cart application shown at the beginning of this chapter with some slight modifications.

  1. Create a new file called recipe31_cart.py and copy all the code from cart.py created in the introduction of this chapter.
  2. Alter __init__ to add an extra storer attribute used for persistence.
    class ShoppingCart(object):
        def __init__(self, storer=None):
            self.items = []
            self.storer = storer
  3. Add a store method that uses the storer to save the cart.
        def store(self):
            return self.storer.store_cart(self)
  4. Add a retrieve method that updates the internal items by using the storer.
        def restore(self, id):
            self.items = self.storer.retrieve_cart(id).items
            return self

The specifics of the API of the storer will be given further down in this recipe.

We need to activate our virtual environment and then install mockito for this recipe.

  1. Create a virtual environment, activate it, and verify the tools are working:
    Getting ready
  2. Install mockito by typing pip install mockito.

Install pinocchio and figleaf using the same steps from the Writing a testable story with Voidspace Mock and nose recipe.

How to do it...

With the following steps, we will explore how to use mocking to write a testable story:

  1. In recipe31_cart.py, create a DataAccess class with empty methods for storing and retrieving shopping carts.
    class DataAccess(object):
        def store_cart(self, cart):
            pass
    
        def retrieve_cart(self, id):
            pass
  2. Create a new file called recipe31.py for writing test code.
  3. Create an automated unit test that exercises the cart by mocking out the methods of DataAccess.
    import unittest
    from copy import deepcopy
    from recipe31_cart import *
    from mockito import *
    
    class CartThatWeWillSaveAndRestoreUsingMockito(unittest.TestCase):
        def test_fill_up_a_cart_then_save_it_and_restore_it(self):
            # Create an empty shopping cart
            cart = ShoppingCart(DataAccess())
    
            # Add a couple of items
            cart.add("carton of milk", 2.50)
            cart.add("frozen pizza", 3.00)
    
            self.assertEquals(2, len(cart))
    
            # Create a clone of the cart for mocking
            # purposes.
            original_cart = deepcopy(cart)
    
            # Save the cart at this point in time into a database
            # using a mock
            cart.storer = mock()
    
            when(cart.storer).store_cart(cart).thenReturn(1)
            when(cart.storer).retrieve_cart(1). 
                                   thenReturn(original_cart)
    
            id = cart.store()
    
            self.assertEquals(1, id)
    
            # Add more items to cart
            cart.add("cookie dough", 1.75)
            cart.add("ginger ale", 3.25)
    
            self.assertEquals(4, len(cart))
    
            # Restore the cart to the last point in time
            cart.restore(id)
    
            self.assertEquals(2, len(cart))
    
            verify(cart.storer).store_cart(cart)
            verify(cart.storer).retrieve_cart(1)
  4. Run the test using nosetests with the spec plugin.
    How to do it...

How it works...

This recipe is very similar to the earlier recipe Writing a testable story with voidspace mock and nose. For details about mocking and the benefits with regards to BDD, it is very useful to read that recipe.

Let's compare the syntax of Voidspace Mock with mockito to get a feel for the differences. Look at the following voidspace mock block of code.

        cart.storer.store_cart = Mock()
        cart.storer.store_cart.return_value = 1
        cart.storer.retrieve_cart = Mock()
        cart.storer.retrieve_cart.return_value = original_cart

It shows the function store_cart being mocked.

        cart.storer = mock()

        when(cart.storer).store_cart(cart).thenReturn(1)
        when(cart.storer).retrieve_cart(1). 
                               thenReturn(original_cart)

Mockito approaches this by mocking out the entire storer object. Mockito originated as a Java mocking tool, which explains its Java-ish APIs like thenReturn compared with voidspace mock's Pythonic style of return_value.

Some find this influence from Java on Python's implementation of mockito distasteful. Frankly, I believe that is insufficient reason to discard a library. In the previous example, mockito records the desired behavior in a more succinct fashion, something that would definitely offset the Java-like API.

See also

Writing a testable story with voidspace mock and nose

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.186.219