What is unit testing?
Your entire software system will only work well if the individual parts are working well.
Testing is one of the key parts of your software development workflow when you are building a software system.
As a software developer, you want the programs you write to work as expected. You want the programs you write to be bug-free so that only the best product reaches the end-user. Testing can help give you confidence that your code works as expected.
In this article, we are going to discuss a testing approach known as unit testing. What are unit tests? How should you implement them? What are their benefits and limitations?
What are unit tests?
The goal of unit testing is to isolate each part of a program and show that the individual parts work as expected.
Unit tests are automated tests written and run by software developers to ensure that a section of an application (known as the unit) works as expected.
A unit is the smallest piece of code that can be logically isolated in a system. This may be an individual function, method, procedure, module, class, or object. Generally, a unit has a few inputs and a single output.
Unit testing is done during the development (coding phase) of an application by the software developers.
The entire system will only be able to work well if the individual parts are working well. By writing tests for the smallest testable units, software developers build confidence that the entire system will work as expected.
Once a software developer writes a unit test they can run it on their local machine to check if the test passes. They can also run all the other pre-existing tests to check if they are still passing.
Software developers generally use a unit test framework to develop automated test cases for unit testing. Unit test frameworks are software tools to support writing and running unit tests, including a foundation on which to build tests and the functionality to execute the tests and report their results.
There are unit testing frameworks for most popular programming languages. Some examples of popular unit test frameworks are Jest for Javascript, JUnit for Java, and NUnit for all .Net languages.
During test case execution, frameworks log tests that fail any criterion and report them in a summary. Depending on the severity of a failure, the framework may halt subsequent testing.
Unit tests can also be set up to be executed on each new build before code is released to a Staging or Production environment. In this way, software developers can ensure only code that passes all the unit tests makes it to a live environment. If any unit tests fail during the build process software developers can fix the issue first before attempting to release again.
Unit testing example
Below is a very trivial example of how unit testing could work in Javascript.
We have a simple sum function that receives two numbers as arguments and returns the result of adding the two numbers together.
function sum(a, b) {
return a + b;
}
module.exports = sum;
Our very simple first unit test of the sum function could be as follows.
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
The above unit test documents its expectation ('adds 1 + 2 to equal 3')
, then executes the sum function with test input arguments 1 and 2. We expect the result to equal 3 after the function execution.
If the result is not equal to 3 the test will fail and we will know our implementation of the sum function has a bug somewhere. ❌
If the result is equal to 3 our test will pass. ✅
We can then add more unit tests for the sum function with other example inputs to cover different cases, such as negative numbers or invalid input arguments to check if errors are handled correctly.
This is a very trivial example, however, it showcases in practice how unit tests work.
Unit testing benefits
Unit testing is a software testing method in which individual units, components, or modules of software are tested to determine if they are fit for use. As a result, it has many benefits. As a software system grows, software developers benefit more from having unit tests in place. If proper unit testing is done in early development, it will save software developers time and money in the end.
Unit testing finds problems early in the development cycle. This includes bugs in the software developer’s implementation. The cost of finding a bug before coding begins or when the code is first written is considerably lower than the cost of detecting, identifying, and correcting the bug later.
Unit testing helps software developers create better software designs. Code can be impossible or difficult to unit test if poorly written, thus unit testing can force software developers to structure functions and objects in better ways. The process of writing a thorough set of tests forces the software developer to think through inputs, outputs, and error conditions, and thus more crisply define the unit's desired behavior.
Due to the modular nature of the unit testing, software developers can test parts of a software system without waiting for others to be completed. This will help software developers create their software in small agile increments.
Unit testing allows software developers to easily refactor code or upgrade system libraries at a later date, and make sure the existing code still works correctly. Any changes software developers make that cause existing unit tests to fail can be identified quickly and addressed. Unit tests detect changes that may break existing working code.
Unit testing creates documentation of the system as a positive side-effect. Software developers looking to learn what functionality is provided by a unit, and how to use it, can look at the unit tests to gain a basic understanding of the unit's interface (API).
Unit testing limitations
Despite the valuable benefits of unit testing, it has some limitations.
Unit testing will not catch every error in the software system. It is very difficult to evaluate every execution path in a software system unless it is a very simple system. Unit testing is limited to only testing the functionality of the units themselves. It will not catch other errors such as integration errors or performance errors.
It can be difficult to set up unit tests if a core function of the unit at test is to interact with something external to the system. Something external such as a database, the file system, or an external API, can present a challenge when unit testing. Software developers will likely have to mock interactions with the external party and this may be challenging and not exhaustive enough as a test.
Final thoughts
Writing unit tests as part of the development of a system can often feel costly and slow. More code needs to be written and it takes time and effort to write good unit tests. A common mistake development teams make is to skip unit testing.
As most software systems grow they will benefit from a wide unit test coverage. Unit tests help make it easier to add new features, refactor existing features, and understand existing individual components of a system.
Adding unit tests early on will save software developers time and money in the end.
Are you writing unit tests in your codebase?