How Python Made A Mockery Of Me

A gotcha when generating code coverage with mocks in Python

python

I’m a Java guy originally but lately I’ve been rolling up my sleeves and wrestling some pythons.  Despite some early “crybabyness” that I didn’t like the environment and that I wasn’t alone, things soon settled into a rhythm that seemed to be working, until I got mocked.

I’m talking of course about the python mocking package, useful when writing tests.  In my case I was trying to generate code coverage when running nosetests.

Running nosetests with coverage is pretty simple:

nosetests --with-coverage --cover-erase --cover-package=some_package --cover-inclusive --cover-branches --cover-html --cover-html-dir=coverage-result

Unfortunately no matter what I did in the test, I always just got this error:

 some_package.some_module  NoSource: No source for code: '/some_path/some_package/some_module.py'

Now it might seem obvious from the error what the problem was, but in my case the “/some_path/some_package/some_module.py” did exist, so it had to be something else.

Immediately assuming the problem must be in the nosetest package as it “could not possibly be my own code” I set about creating a sample case to let the folks at nosetests know just how much smarter than them I truly was.

Naturally my sample test case worked as it should.    It wasn’t nosetest.

It turns out the problem was my use of the Mock object in python.  My test was verifying some file system operations and I needed to make sure that under certain conditions a check for the existence of a file would return “False”, so I had code in the test that looked like this:

from mock import Mock
...
os.path.exists = Mock()
os.path.exists.return_value = False

Well it turns out that this changes the behavior of the “os.path.exists” method for everyone, which when you consider that this is a global python library it makes complete sense.  As a result the coverage process that is supposed to produce output files was getting all confused.

The correct way to mock out objects within a unit test is using the “patch” syntax around the test methods itself:

from mock import patch
...
@patch('os.path.exists')
def test_some_file_awesomeness(self, mockPathExists):
mockPathExists.return_value = False
...

This results in the patching of the os.path.exists method being scoped to the test, and only the test.

So naturally this can only mean one of two things:

1. Python is a terrible language that should be taken out and shot

2. I need to RTFM

I’ll leave it to you to decide.

Advertisement

2 thoughts on “How Python Made A Mockery Of Me

  1. Pingback: The Socialize Blog » How Python Made A Mockery Of Me

  2. Pingback: How Python Made A Mockery Of Me | ShareThis.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s