Welcome to your third instalment of the fantastic DevOps Blog Series, written by UKFast DevOps Engineer Tim. Today, we’ll get straight into the world of Unit Testing and Test Driven Development (TDD).
Over to you, Tim.
Any developer knows how easy it is, while fixing one problem, to generate another.
This is often due to something like accidentally deleting or moving a piece of code. Or, in format-sensitive languages like Python, due to accidentally indenting or out-denting a piece of code. Similarly, refactoring code (i.e. improving the internal structure of code) can often have unexpected side effects.
These typos wouldn’t be so bad if they caused the code not to run at all. However, they often introduce some subtle bug into code that has already been tested, and these bugs are often only noticed once the software has been deployed.
But as any project manager knows, it’s much easier and cheaper to fix bugs before stuff ships. So, this is where comprehensive regression testing comes into play.
Tools like Git are very useful at highlighting differences accidentally introduced at some random point in the source code. But, particularly on a complex project, it’s entirely possible that such changes will be missed during the code-review process. An increasingly popular technique is using a unit-testing framework as part of a general commitment to Test Driven Development (TDD). TDD goes some way to breaking the vicious cycle below.
TDD is used extensively at UKFast, on products like our MyUKFast portal, and is actually nothing new. It was a fundamental part of Extreme Programming (XP), which was developed in the 1990s. It has also been regarded as good practice since NASA’s Mercury Space Programme all the way back in the 1960s.
Unit testing involves running a series of tests on each “unit” of software. A unit is the smallest testable piece of code and is usually a function or similar. Effective unit testing is essential as this is the foundation on which further testing is built. A developer should, of course, perform unit testing as part of the normal development process.
In older languages like C or C++, this was usually just done at development time or had to be done via some sort of external (i.e. non-standard) extension like the C++ Boost libraries. More modern languages like Python or Go have unit test frameworks built in as part of the language – for example, Python’s unimaginatively named unittest library – or a variety of external ones.
Unit test frameworks
Unit test frameworks allow the programmer to specify a series of assertions about how the software is expected to behave under various circumstances.
One assertion might be that attempting to open a non-existent file will result in a particular exception to be generated or that a function called with particular parameters returns a particular value.
These unit tests can then be run every time the code is built or committed to the repo. This gives the continual assurance that the code works as expected and no regression has been introduced. This is particularly useful in refactoring code. It is also significantly faster and more comprehensive than manual testing could ever hope to be.
Of course, a cynic would point out that this doesn’t necessarily mean that the code is right, merely that it passes the tests we have written for it. But an imperfect test is usually better than no test at all.
Unit tests are often added as an afterthought, before the code is committed (and I am as guilty of this particular sin of omission as anybody). But, in principle, they should be written before the code itself. The idea being that initially the unit tests will fail but as the code is developed, more and more of the unit tests will be passed. As soon as the all the unit tests for a particular unit are passed, then that unit is functionally complete.
From a business perspective, unit testing and TDD allow for much faster turnaround of code with fewer errors and greater assurances of code quality. It’s therefore highly valuable for an organisation to inculcate a culture of TDD.
Catch up with the rest of the DevOps Series to find out more about the hottest DevOps topics.