Friday, November 18, 2011

Configuring Eclipse for TDD in Python: The Nosetests way

In my daily work, Python is my main (and really beloved) programming language. For coding in Python, I usually work with vim in remote machines and Eclipse IDE in my desktop one. In the next sections I'll explain how to convert Eclipse in the killer TDD Python-IDE, but you can have a look at the official Python Wiki for more options: Python IDE's and Python Editors.

1. Installing Eclipse
In the Eclipse downloads section, get the Eclipse Classic package that corresponds to your platform (I'll focus on Linux in this post, but it should be easy to follow the same steps in Windows and Mac OS platforms as well).
Installing Eclipse is as easy as extracting the downloaded package if you have an existing Java Virtual Machine installed in your system. If not, check this FAQ.

2. PyDev
PyDev is a Python IDE for Eclipse with a lot of goodies such code completion, syntax highlighting, debugger, unit testing support and refactoring options. You can find a very nice installation manual in the offical project page: PyDev Manual (Installing). For lazy readers, it should be as easy as opening Eclipse, go to Help > Install New Software, adding a new software location with the Add button and fill in the location field with the url http://pydev.org/updates. Once added, check PyDev option and follow the instructions.

3. Nosetests
python-nose or nosetests is an unit test-based framework that is capable of discover and execute your tests without having to register them first. Nose makes my life much easier writing test functions to check for exceptions and discovering and executing my test packages. However, I strongly suggest you to read this article to understand which are the main motivations to use a unit test framework and why choosing nose.

Nose is available via setuptools:
$ easy_install nose

Now try to import nose in python:
$ python
Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import nose
>>>

If nose installation is fine, you shouldn't see any error messages after importing the nose package in the Python console.

If you don't have setuptools installed in your system, there are a few more options to install nose package, check the official installation guide.

4. Configuring Eclipse for continuous-testing
First of all, create a new PyDev project: File > New > Project, navigate to PyDev and choose "PyDev Project". Click "Next", choose a project name (in our case "tdd-example"), select Grammar Version as 2.6 or 2.7 (nose supports Python 3 syntax, but many plugins not) and select the option "Add project directory to the PYTHONPATH?". Finally, click "Finish". You should see now in the PyDev Package Explorer view your new tdd-example project.

Now, go to RunExternal Tools > External Tools Configurations. In Program option, create a new one. Name could be "NoseTestTool", working directory will be "${project_loc}" and in location the path to nosetests binary, in my case "/usr/local/bin/nosetests".

Right click in the project tdd-example > Properties and navigate to Builders. Click in Import and choose the previous created NoseTestTool. Make sure is the first option selecting it and clicking the Up button. Now, click in Edit. In the Main tab, add in Arguments field "--processes=4" where processes is the number of simultaneous threads that will be used to execute your tests. In my case, I use 4 cores, so I set processes to 4.
In the Environment tab, create a new variable with name "PYTHONPATH" and value "${project_loc}".
In Build Options tab, check the options "Allocate Console (necessary for input)", "Launch in background", "After a Clean", "During manual builds" and "During auto builds".

To test that everything is configured fine, create a new Python file in your project, test_all.py and include the following code:

def test1():
    assert True
    
def test2():
    assert False 

At the time that you save the file, nose may execute your tests automagically and an ouput like this should be shown:

.F
======================================================================
FAIL: test_all.test2
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/lib/python2.6/dist-packages/nose-1.1.2-py2.6.egg/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/dev/workspace/python/tdd-example/test_all.py", line 6, in test2
    assert False
AssertionError

----------------------------------------------------------------------
Ran 2 tests in 0.002s

FAILED (failures=1)

Nose searches for files and functions that start with "test*". In our case, two functions will be tested, test1 and test2, and one of them will fail (assert False) as seen in the previous output.

I hope this howto will motivate people working with continuous TDD in Python :)

1 comment: