Friday, February 4, 2011

Mocking libcloud using Mox

If you're using libcloud to interact with cloud providers, you probably would like to cover this code with tests if you haven't done so already.

However, most likely you don't want libcloud to do any real calls to the cloud provider API service as it could create additional noise on your account, add extra load and cost you money (esp. if tests are being run on a regular basis on a Continues Integration server).

One possible solution is to mock these things up using Mox mocking framework. Libcloud uses 2-step initialisation process for a connection class -- first you obtain driver class and then initiate it -- and I seem to always forgot how to mock it right, so decided to write it down.

For example, we have a simple class that returns a list of names of currently available nodes:



And we want to test it without making actual API calls. Let's see how the test code will look for this task. For the sake of simplicity default unittest framework will be used.



Basically, what we are doing here in the test:


  • Create a mock for RackspaceNodeDriver class and record list_nodes() call for it; we also make it return two node objects. Actually, we create fake objects using type(), so we don't have to bother with creation of real Node objects which need a way more properties to be set when constructing that we don't care about

  • Use mox's StubOutWithMock routine (please check mox docs for details on that) to stub out libcloud.providers module. Here, in get_driver() we pass lambda because get_driver() returns class itself, not an instance of the class, and using lambda here seems to be more clear than recording __call__() for a class

  • All the standard ReplayAll/VerifyAll mox routines


For the sake of experiment, you can replace actual node_names() method of the Example class with just return ["node1", "node2"]. The assertEqual() will pass, but mox will complain that list_nodes() wasn't called and test will fail.

1 comment:

  1. This comment has been removed by a blog administrator.

    ReplyDelete