Python is a language that some people like and use. I recently was given a small Python application and had to refactor it so that I could implement unit and functional tests. It took me a while to find the resources and learn how to implement them, so I thought I’d write it up here.

* Please note that this is not a unit testing tutorial. Those are plentiful and can be found elsewhere. This describes setting up a professional Python project with a real package structure, injection and tests.

Python Project Structure

Python was conceived as a scripting language. Unlike compiled languages like C or Java, Python files are designed to be directly runnable.

RunMe.java -p someargument< Not ok!

RunMe.py -p someargument< Ok!

This is convenient for small scripts, but doesn’t help applications. Since you are intended to run .py files directly, Python projects should not have source (src) or binary (bin) folders. Having a source folder makes a program difficult to run. The root of the Python package structure should be the root of the project.

For the same reason, the test folder should not be separate. It should be a package in the main project. Since the root folder is the root package, you won’t have much choice.

Example

MyProject

__init__.py

model

__init__.py

user.py

service

__init__.py

user_service.py

ldap_user_service.py

test

__init__.py

unit

__init__.py

service

__init__.py

user_service_test.py

functional

__init__.py

service

__init__.py

user_service_test.py

If you’re new to Python, please note that those __init__.pys are required. They tell Python which folders contain Python files. This is Python’s way of allowing you to have other folders, such as config, that don’t contain source files.

Note

You should specify exactly one class per file. It makes your code easier to find and read.

Injection with snake-guice

Injection is essential for unit tests. In the example above, LDAPUserService may connect to an LDAP server to retrieve information, something we don’t want to happen in unit tests.

Install snake-guice, Mock and nose

If you don’t have it, grab a copy of easy install. Then, $ sudo easy_install.py snake-guice Mock nose. That installs packages from PyPI, the Python Package Index. For those used to non-scripting languages, this may not seem like a great way to use external libraries. Since Python is a scripting language, however, it’s the only way that makes sense.

Running Tests

For running tests, I recommend Nose. To run your tests, open a command line and change to your root project folder. Then, nosetests. Nose will take care of adding the packages you installed earlier, snake-guice and Mock, to the Python path. It will also find all tests, run them and report the results. Handy!

$ cd ~/exampleproject
$ nosetests

To run only unit or functional tests, use the -w argument.

$ nosetests -w ./ ./test/unit
$ nosetests -w ./ ./test/functional

Is that all?

To those who have not used injection or mocks for testing, this may seem like a lot of work. I hope you’ll try it, though, because it’s a one-time setup. Once these good practices are in place, you’ll find that all of your code organization and testing becomes simple and almost automatic. If I missed something or if you have any suggestions, please post below.

You are correct. Python has trouble with name conflicts. That’s caused by Python’s runtime and its coding standards. The problem you described would never happen in Java because classes are camel case and packages are lowercase and also because Java doesn’t allow imports of packages. In Python, it’s possible to “import model” where model is a package. Python allows package imports because of those annoying __init__.py files. The only workable solution is to not have name conflicts. I suggest simplifying the package structure by removing the model-specific package.

Howdy would you mind letting me know which hosting company you’re utilizing? I’ve loaded your blog in 3 completely different web browsers and I must say this blog loads a lot faster then most. Can you suggest a good web hosting provider at a honest price? Kudos, I appreciate it!

Hi David.
All of that business about modules is the snake-guice part. It’s very similar to Guice because it uses modules, bindings and code configuration. In this project, we only used it for testing support. We had a unit test module that mocked all HTTP calls. We had another for functional testing that connected to a local test server. The third is used in production to connect to real servers.