Unit Test

Create Python Development Environment Ready For TDD

Create Python Development Environment Ready For TDD

Reading Time: 11 minutes

In this post we will create a development environment for python applications which will support the TDD (Test Driven Development) paradigm. I won’t be telling you to stick to one or another way of programming here, this is your decision, but as a professional programmer you must try at least the TDD. This article is all about technical stuff to get things move and make an easy development environment for you.

The project

We will build a very simple application. Like in most unit tests tutorials we will create a calculator app. This will be our skeleton and from there you can take it to any application you want to create. The application itself is not the issue here, what’s important is the environment suite. We will set the project up for Python projects (with PyCharm over Windows 10 os, we will use Python 3.6) but of course there’s an alternative you can get for pretty much all languages and frameworks.

The steps we’ll take in order to complete our task are:

  1. Create the project
  2. Initialize virtual environment (with virtualenv)
  3. Set up dependencies
  4. Run test with pytest watch

Create the project

  1. Create a project with PyCharm (File -> New Project)
  2. Choose the directory where you want to initialize the project. Create a virtual environment (virtualenv) so that we can isolate our local project dependencies from the global ones.
    Create Project

Initialize virtual environment (with virtualenv)

  1. Install virtualenv (although should be ready for use with PyCharm)
  2. Open your terminal window and type venv\Scripts\activate (this will activate the virtual environment, making all dependencies installation be local to the project)
    Virtual Environment* you don’t have to type the .bat suffix

Setup Dependencies For TDD

  1. Here we will install our development dependencies, these are the tools we will use in order to make or coding faster and better.
  2. I like to create 2 files that will handle my dependencies, one is for the development dependencies (such as test runners, linters, etc) and one for production. The pip package manager (which we use in our project) uses the convention of managing the dependencies in a file called requirements.txt, so we’ll create requirements.txt and requirements-dev.txt to distinguish between dev and production.
  3. We will focus on the requirements-dev.txt in this article. We want to install a tool which will run our test continuously so we won’t need to run the tests manually after we finish coding, the test will run automatically for us as we code and with every files save (ctrl+s) the tests automatically will prompt the results on the terminal screen. The tools I would like to introduce you to run our unit tests are Pytest and Pytest-watch (those together run the tests for us, prompt the results in a readable way and make this process continuous). Please add those lines to requirements-dev.txt

    Those are the latest version as for the date this post is published, if you want to install the latest versions just remove the ==VERSION_NUMBER from each line.
  4. The next tool we want to use is a Linter. A linter is a tool that can help you to analyze you code (such as formatting) which eventually minimize the chance to get errors. For python projects, I think the most popular linter is pylint, but PyCharm’s inner pre-installed linter should cover most of the code analysis pylint does.
  5. The requirements-dev.txt file should look like this:
    Dependencies
  6. install the dependencies by running pip install -r requirements-dev.txt (or if you use python3 py -3 -m pip install -r requirements-dev.txt)

Run tests with pytest watch

Now is the interesting and productive part and surprisingly (or not) the easiest one. I won’t be talking about how to develop using TDD in this post, but lets try something very simple to demonstrate the power of TDD. I won’t be talking also about the unittest framework from the standard python lib. Just show how to get it work.

  1. Create a test directory and add a test file in there, for example test_math.py (this should be a convention, write your tests under test directory, and prefix the test files with test_ prefix)
  2. Open the terminal and type ptw, this will run you test with pytest and run it continuously with pytest-watch on every file save.
  3. Import unittest and add a test (a test is a class that starts with the prefix Test, so let’s call it TestMath and it should extend the unittest.TestCase class).
  4. Let’s add a test_math_add method to test an add method with some simple assertions, and import out not yet implemented math library.
  5. Add a main function (also unittest convention)
  6. Your code now should look like this:
    import unittest
    from lib import math
    
    
    class TestMath(unittest.TestCase):
    
        def test_math_add(self):
            self.assertEqual(10, math.add(6, 4))
            self.assertEqual(0, math.add(1, -1))
            self.assertEqual(0, math.add(-1, 1))
            self.assertEqual(-2, math.add(-1, -1))
    
    
    if __name__ == '__main__':
        unittest.main()
    
    
  7. now you should see our tests fail in the terminal and that’s fine because we did not yet implement our library.Tests Fail
  8. let’s add lib directory and math.py module with the following contents:
    def add(x, y):
        return x + y
    
  9. Tests should pass now. (Please make sure the test and lib directories both have __init__.py file so they could be used as modules)
    Tests Pass

 Conclusion

You can take it from here and use it as a skeleton for projects you want to implement by TDD. I didn’t get into the internals of TDD paradigm but there is so much information out there about it you can find easily. Try making you own small project and follow Uncle Bob’s 3 rules of TDD:

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

After few tries you get the trick and start developing clean[er] code programs.

Fell free to explore the GitHub repository of the project https://github.com/dimagimburg/python-continuous-tdd-skeleton

Cheers.