Unit testing is vital in software development as it ultimately aims to deliver superior software products by focusing on testing smaller portions of code. This technique allows developers to analyze code, catch bugs earlier, and fix them faster. In this article, we will define the methodology, its misconceptions, frameworks to use for test automation, benefits, best practices, and finally, leave you with an example of how to perform a unit test.
What is Unit Testing? (Ryo Chikazawa)
January 21, 2021
In software testing, unit testing is a method of testing smaller isolated portions (or units) of code. Unit tests are usually conducted with test automation scripts on the smallest testable portion of the software.
The unit is usually a function of the code. It is also a part of “white box testing,” which is a type of testing that can be written by someone who knows the architecture of the program.
This methodology can test code functions, procedures, or methods whether utilizing procedural programming or object-oriented programming. If it relies on or talks to any other systems, then it cannot be qualified as unit testing. The purpose is to ensure that each unit of code functions as expected. This allows for quality assurance to write test cases only for portions of the software that affect the behavior of the system.
What unit testing is not…
Unit testing cannot be performed on every part of the software. Nor can it have dependencies on other systems to be considered of the ilk. In his book “Working Effectively with Legacy Code” Michael Feathers explains why. “If it talks to the database, it talks across the network, it touches the file system, it requires system configuration, or it can’t be run at the same time as any other test.”
Here are a few examples of common misconceptions:
- Tests that require requests to a service you wrote, these tests are known as smoke tests.
- Tests that call to an outside network and get halted by firewalls. These tests which reach outside and test the totality of the application are known as end-to-end (or E2E tests.)
- Tests that ensure new functionality have not adversely affected previous code. This is called regression testing.
Why is unit testing important?
Unit testing is very important as it allows developers to detect bugs earlier in the lifecycle- thus improving the quality of delivered software. Here is a list of great benefits:
- This methodology can reduce the overall impact on testing costs as bugs are caught in the early phases of development.
- It allows better refactoring of code as it is more reliable code.
- This practice also conditions developers to rethink how they code. Meaning, coding modular components that can be mocked if they do have dependencies.
- Tests can be automated, which is extremely beneficial when maintaining code at scale.
- Overall, it improves the quality of the code.
In DevOps, the process of continuous integration automatically runs tests against the code every time someone commits new code to the repository. If one test fails, the entire team can receive an email (or alert on Slack) of the break. Then the responsible person can rectify the issue.
Top unit testing frameworks
There are a number of testing tools developers can use for testing. Here is a list of top frameworks for unit testing:
- JUnit – a simple, open-source framework to write and run repeatable tests.
- NUnit – a unit testing framework for all .Net languages. It initially derived from JUnit, however, completely rewritten and features were expanded for .Net projects.
- xUnit – an open-source unit testing tool for .NET Frameworks, it was written by the original author of NUnit.
- TestNG – a testing framework inspired by JUnit and NUnit yet with more powerful features.
- RSpec – a BDD testing framework for Ruby.
- PyUnit – a standard unit test framework for Python.
- PHPUnit – a programmer-oriented unit testing framework for PHP.
Unit testing best practices
There are some best practices developers should follow when writing unit tests for future scalability. Here is the list of best practices:
- Tests should be easy to write and not amount to huge efforts.
- Tests should be readable. When done correctly, a developer should be able to fix a test without debugging code. This saves time and effort.
- Unit tests should be reliable. For example, tests may pass on a development machine yet fail on a continuous server. Practicing good design flow can help alleviate these stresses.
- They should be fast. When you consider the quantity of unit tests your software will accumulate as it scales, waiting on slow tests is counterproductive.
- Tests should be unit and not integration. There is a major difference between the two. Integration in contrast to unit seeks to simulate a user’s environment and test across outside dependencies- such as a call to a database. See more elaboration below…
Unit tests vs Integration tests
Integration testing is a methodology ensuring that the application or the external integrations of that application are working properly. This test layer tries to emulate real-life environments for testing an application. Whereas, unit tests check the smallest possible portion of code without dependencies. This is an assembly layer, normally reserved for engineers who know how the code works. For example, API testing is considered integration testing, as it calls different modules inside of an API.
In the diagram above, we speak of Functional Testing as well (more on that in a related article.) Functional testing examines the functionality of the code without peering into the internal systems. It is known as “black-box testing.” Whereas the tester does not have to possess detailed knowledge of how the system application works in order to perform testing.
The pyramid above illustrates the quantity of tests in relation to the application. Unit tests should make up the majority of your automated testing, followed by integration and functional testing.
Real-world examples of Unit, Integration, and Functional Testing
Unit testing examples – testing for power to the circuit board, testing if the dialer app can execute, testing if a SIM card is inserted, etc.
Integration testing example – testing if the SIM card is activated or testing if the device has a mobile data connection.
Functional testing example – testing that a phone can make a call.
How to write unit tests
To start writing unit tests, you should choose a unit testing framework first. Some are listed here for your reference.
There are mainly two approaches, bottom-up or top-down. Most of the functions call other functions in its code. i.e. function A calls function B and function B calls function C. The top-down approach is writing tests from function A while the bottom-up approach is starting from function C.
When developing, run a unit testing framework in your local machine. When you commit code to your team’s code repository, the test should be executed too. It is wise to set up a CI tool, like Jenkins or CircleCI to continuously run tests.
Unit test example
Here is a simple example of how to write unit tests…
Let’s say we’re trying to implement the
It takes two numbers
b as its arguments and returns the number of the total amount.
def sum(a, b): return a + b
The simplest way to write a unit test is by using the
assert function. This function can be found in most programming languages.
# It should pass assert sum(1, 2) == 3 # It also should pass assert sum(1, 2) != 0
If your shop is performing automated tests on a consistent basis, you can see how beneficial unit testing is for catching bugs early on. Without this technique, a defect could make its way farther into the pipeline. Even worse, into production.
This means time and resources are allocated to finding, analyzing, and fixing defects when a simple automated test could have caught them. If your firm seeks a test automation tool to automatically catch bugs and alert developers via Slack, consider using Autify.