views:

35

answers:

3

In my Django application, I have a section of code that uploads a file to Amazon S3, and I would like to skip this section during unittests. Unittests happen to run with DEBUG=False, so I can't test for settings.DEBUG == True to skip this section. Any ideas?

+1  A: 

You could -- and yes, this is a hack -- import the module that does the uploading, and replace the upload function in that module with another function, that does nothing. Something like this:

foo.py:

def bar():
    return 42

biz.py:

import foo
print foo.bar() # prints 42
foo.bar = lambda: 37
print foo.bar() # prints 37

Again, it's a hack, but if this is the only place where you're going to need such functionality it might work for you.

Thomas
I accepted rcoder's answer as the best solution, but I ended up using this one, because doing it the "right" way doesn't matter in this specific situation.
Christian Oudard
No worries, his answer is more right than mine anyway :)
Thomas
+1  A: 

You really don't want to "skip" code in your unit tests -- if you do, you'll never have coverage for those areas. It's far better to provide a mock interface to external systems, so you can insure that the rest of the code behaves as expected. This is especially critical when dealing with external resources that may be unavailable, as S3 can be in case of network issues, service interruptions, or configuration errors.

Alternately, you could just use the Django S3 storage backend in your production environment, while configuring tests for use local file storage instead.

rcoder
A: 

You don't skip a function for testing.

You provide a mock implementation for something that you don't want to run as if it were production.

First, you design for testing by making the S3 Uploader a separate class that has exactly the API your application needs.

Then you write a mock version of this class with the same API. All it does is record that it was called.

Finally, you make sure your unit test plugs in your mock object instead of the real S3 Uploader.

Your Django application should not have any changes made -- except the change "injected" into it by the unit test.

Your views.py that does the upload

import the_uploader
import mock_uploader
from django.conf import settings
uploadClass = eval( settings.S3_UPLOAD_CLASS_NAME )
uploader= uploadClass( ... )

Now, you provide two settings.py files. The default settings.py has the proper uploader class name.

For testing, you have a test_settings.py which looks like this.

import settings.py
S3_UPLOAD_CLASS_NAME = "mock_uploader.mock_upload_class"

This allows you to actually test everything.

S.Lott
In this case, it makes for quite a natural separation of responsibility. But I've seen code where the structure of the main code was cluttered by interfaces with only one possible implementation, that had no purpose except to make the unit tests happy.
Thomas
@Thomas: In Python? Or Java/C# where the extra declarative stuff is required?
S.Lott