Replacing module with a stub for unit testing

Fuzzyman fuzzyman at gmail.com
Thu Jun 4 09:52:32 EDT 2009


On May 23, 2:00 pm, pigmart... at gmail.com wrote:
> Hi,
>
> I'm working on a unit test framework for a module.  The module I'm
> testing indirectly calls another module which is expensive to access
> --- CDLLs whose functions access a database.
>
>     test_MyModule --->MyModule--->IntermediateModule---
>
> >ExpensiveModule
>
> I want to create a stub of ExpensiveModule and have that be accessed
> by IntermediateModule instead of the real version
>
>     test_MyModule --->MyModule--->IntermediateModule---
>
> >ExpensiveModuleStub
>
> I tried the following in my unittest:
>
>     import ExpensiveModuleStub
>     sys.modules['ExpensiveModule'] = ExpensiveModuleStub # Doesn't
> work
>
> But, import statements in the IntermediateModule still access the real
> ExpensiveModule, not the stub.
>
> The examples I can find of creating and using Mock or Stub objects
> seem to all follow a pattern where the fake objects are passed in as
> arguments to the code being tested.  For example, see the "Example
> Usage" section here:http://python-mock.sourceforge.net.  But that
> doesn't work in my case as the module I'm testing doesn't directly use
> the module that I want to replace.
>
> Can anybody suggest something?
>


My Mock module, and in particular the patch decorator is designed
explicitly to do this.

You need to be careful because modules and module level globals
(including things your module imports) are global state. If you change
them for the purpose of a test you must *guarantee* to restore them
after the test.

http://www.voidspace.org.uk/python/mock/
http://www.voidspace.org.uk/python/mock/patch.html

In your case if you are testing a module which imports ExpensiveModule
then by ExpensiveModule lives in the *namespace of the module under
test*. You could patch it like this:

from mock import patch
import module

@patch('module.ExpensiveModule)
def testModule(self, mockExpensiveModule):
    ....

Whilst the test is running 'module' has'ExpensiveModule' mocked out
(replaced) with a Mock instance. This is passed into your test so that
you can setup behaviour and make assertions about how it is used.
After the test is completed the patching is undone.

All the best,


Michael Foord
--
http://www.ironpythoninaction.com/




More information about the Python-list mailing list