Python Unit Testing - Why Do It?
It’s not a secret that an absolute key to developing quality software is to test your code as you write it. After all, source code of your Python application must be bug-free to function correctly, but all human beings generate bugs at a very high rate when writing code.
Python unit testing should be performed starting from the first stages and throughout the whole process of development. Unit tests are written to detect bugs early in the development of the application when bugs are less frequent and less expensive to fix. However, if unit tests are not designed well, or they are run incorrectly, bugs go through into the production stage. Skipping unit testing in the development process will surely cost your company thousands, if not hundreds of thousand of dollars depending on the scale of the project.
Python Unit Testing
A unit test is a scripted code-level test, written in Python to verify a small "unit" of functionality. Unit tests, in general, are often designed to provide a broad testing coverage of the web application, including any weird corner cases and some tests that should not work. Their interaction with external resources like the disk, the network, and databases is set to the minimum; testing code that is dependent on these resources is usually put through functional tests, automated acceptance tests, or integration tests.
Goal of python unit testing is to detect as many bugs and inconsistencies in the infancy of the application development as possible. This is achieved by designing and scripting accurate and quality unit tests that can also serve as detailed documentation for the development process. This ensures that bugs and other problems we catch in the first stages of the development, can be fixed by the development team.
Unit Testing Techniques
Unit testing with Python focuses mostly on testing a particular component without accessing any dependent objects. This is often difficult to do and a bit unrealistic as code is inherently dependent on other piece of code. Python developers can use techniques such as stubs and mocks to separate code into “units” and run unit level testing on the individual pieces, but first let’s look into what these techniques involve.
Test-Driven Development TDD
Unit testing should be run along with the Python development; you can and should execute unit tests as you're developing each new feature. To test the application robustly development teams follow so-called test-driven development (TDD) approach: for each new functionality that your application must have, you first design Python unit tests and only then do you carry on writing the code that will implement this feature. TDD may seem like developing Python applications upside-down, but it has some benefits. For example, it ensures that you won't overlook unit testing for some feature. Further, developing test-first will help you to focus first on the tasks a certain function, test suite, or test case should achieve, and only afterwards to deal with how to implement that function, test suite, or test case.
Stubs and Mocks
In good development practice, Python unit tests must be executed frequently, so it is grave that they run fast. It's therefore crucial that each unit can assume other units are working properly and as expected.
Stubs and Mocks are two major techniques that are used to fake methods being tested so they think they are using a real system resource. A stub is used to fill in some dependency that is required for a unit to run correctly. So, in order to test an application component, which may be dependent on other components not yet developed, you will need to write stubs, which basically are fake implementations of various components’ interfaces that give correct responses in cases needed to test other units. A mock on the other hand is used more for assisting in verifying that the unit did what it was designed to.
To put it simply, mocks are about behavior and stubs are about state.
Tools and Unit Testing Best Practices
To manage the unit testing process efficiently, Quintagroup recommends using testing frameworks. Fortunately, Python makes testing truly easy and convenient with its PyUnit, doctest module and nose. They allow you to gain a better insight into the management and development process of your project, thus resulting in improved quality, minimum bugs and shortened delivery time as well as lessened amount of effort needed for the project.
Python Unit Testing Frameworks
Python unittest/PyUnit - Automated testing framework
Python's unittest framework, often referred to as PyUnit, is based on the mature XUnit framework developed by Kent Beck and Erich Gamma. PyUnit supports fixtures, test suites, test cases and a test runner to enable automated testing for your code.
The unittest module is sophisticated and flexible: you can organize test cases into suites (in separate classes and even separate files) with the same fixtures, meaning common setup / tear-down for all test cases in a suite. Python unittest uses standard methodology from Java (JUnit).
Nose extends Python unittest to make testing easier. Nose comes with a number of built-in plugins to help you with output capture, code coverage, doctests, and more. It also comes with plugin hooks for loading, running, reporting tests. Nose, although fully compatible with Python unittest, has a slightly different approach to running tests. Nose, we may say, lowers the barrier to writing tests. Its syntax is less complicated and as such, you can just start testing code quickly without thinking too much about it.
Doctest – Testing through documentation
Doctest is a simple, easy-to-use unit testing framework, where the testing script goes in docstring with small function at bottom of file that runs it
Doctest module lets you test your code by running examples included in the documentation and verifying that they return the desired results. After parsing the help text, doctest searches for examples, runs them, and finally, compares the output against the expected value. The clear advantage that doctest offers is that your docstrings will serve as real examples, as well as tests of the functions that they document. And examples are the best documentation you may put up for a function.
Best practices followed:
Write tests for parts that have the fewest dependencies on external resources first, and work your way up.
Tests should be logically as simple as possible.
Each unit test should be independent of all other tests.
Each unit test should be clearly named and well documented.
All methods, regardless of visibility, should have appropriate Python unit tests.
Strive for one assertion per test case.
Create unit tests that cover exceptions.
Key advantages of Python unit testing are:
Detecting problems early - Unit tests discloses problems early into the development.
Mitigating change - Allows the developer to refactor the source code during the testing stage and later on, while still making sure the module works as expected.
Simplifying integration - By testing the separate components of an application first and then testing them altogether, integration testing becomes much easier.
Source of Documentation - The unit testing with doctest provides a better insight.
Also unit tests can be run quickly, easily, and as frequently as the project requires it. They serve as executable documentation for the application, and they also serve as an invaluable reminder of bugs you've fixed in the past. As such, they decrease the development time, thus allow to quickly deliver functional code - and that's really the bottom line.
Not all companies and Python development teams have the expertise, time, or resources to perform quality unit testing themselves. Quintagroup’s unit testing service enables your developers to concentrate on their core activities, while we make sure each and every unit component is tested thoroughly for its performance, and quality throughout the entire development of the project. Quintagroup implements best industry practices, methodologies and verification tools such as PyUnit, and many other industry standards relevant to your project needs.
You will save time and money on a process that might otherwise be resource draining if performed in-house by only the development team.