9 questions about unit testing answered
- What is unit testing?
Unit testing is a coding practice dedicated to verifying the behaviour of a unit of code against its specifications. Effectively, a unit test is nothing other than a piece of code that tests a code (usually a function, method or less frequently, a module), which informs us if they meet their design and behave as intended in different scenarios.
Unit tests are only one of the different types of software tests that should make up a complete testing pipeline. Others include integration tests, functional tests and performance tests.
- When should I unit test?
Unit tests should be written during the development phase, to verify and document the correct behaviour of a function or method.
Many software engineers run their unit tests locally during the development process, but most importantly, unit tests should be run in the CI pipeline to verify that no regressions are introduced in the code.
Studies from Microsoft Research and IBM show the efficiency of early stage testing on the robustness and reliability of softwares.
- Why should I unit test?
Unit tests are an early stage phase of testing that positively impacts the robustness and reliability of software: developers who don’t unit test usually face a significantly higher volume of bugs and software failures. Even though spending more time and resources on unit testing feels like a direct negative impact on the capacity to lead the project to its goal under time and resource constraints, it usually aids in the successful execution of the project. Here are 4 specific dimensions which benefit from unit tests that are recognised by software developers:
- It helps to drive the design of a feature and gain architectural clarity. Developers are forced to put the specifications of the function they are trying to write into code, and to think in terms of scenarios - which helps early detection of potential issues.
- It makes it extremely easy to spot and avoid regressions, thus giving developers confidence that their new feature does not risk breaking some pre-existing behaviours of their program. Being more confident also allows them to build better software architectures.
- It is a quality assurance practice that overall tends to significantly raise the robustness of the code that makes it to production, by highlighting mistakes that would jeopardise the code execution or compilation. Of course, the obvious consequences of this are fewer bugs and happier users.
- Good unit tests also effectively function as pieces of documentation by describing the expected behaviour of a unit of code in different scenarios. These are also valuable sources of information to support refactoring and restructuring efforts.
The ripple effect of unit testing described above impacts the success of a project by reducing unexpected errors and resources dedicated to fixing as well as increasing the success of the software amongst end users.
- Which element should I unit test?
Like the name says, unit tests should test the smallest testable units in your code. Most often, these units correspond to a function or a method.
- What is a good unit test?
A best practice for unit testing is similar to an exhaustive bug report. You should find key elements of comprehension inside it: what is the unit being tested, what is the expected behaviour, what is the actual behaviour and how to reproduce the difference between the two.
In order to write a good unit test you should always keep a focus on simplicity. A unit test should be easy to code, easy to read, stable and as a consequence it should execute very quickly. Since you are testing a single unit your unit test should not access database, file systems or other similar external factors.
Find out more about unit testing best practices in our dedicated blog post: An opening guide to understanding unit testing best practices
- What is the difference between unit tests and functional tests?
Unit tests are written from a software developer’s perspective, and therefore are code-aware: they are very much dependent on the code and data structures that they are testing, because their purpose is to verify the correct behaviour of a piece of code.
On the contrary, functional tests are written from the user’s perspective. As such, they do not depend on the details of the code, as their goal is to verify the correct behaviour of an end-to-end feature or functionality. As such, functional tests do not attempt to isolate units of code, but instead must take into consideration dependencies with web services or databases.
- What is code coverage?
Code coverage is an indicator of the amount of code that is executed when a unit test suite runs. It can be useful to identify which portions of code have not been tested, or haven’t been tested well enough.
Code coverage can be measured based on different criteria: function coverage, statement coverage, path coverage, branch or decision coverage and condition coverage. Test runners usually collect coverage information according to several of the methods above.
It is often nearly impossible to achieve 100% test coverage, therefore, most software companies usually aim at 80%.
- What is mocking?
Mocking is a practice used to isolate units of code that have dependencies on other functions or objects, with several possible objectives:
- Controlling the return value of a function call
- Avoiding unwanted side effects of a function call
- Forcing a function into a deterministic behaviour to ensure it can be tested reliably
The term mocking, however, is rather generic - as it refers to a range of techniques which help achieve the goal of code isolation. The most common amongst these are stubs and spies. Stubs replace the function call and its return with a simplified one defined by the developer, while spies allow to make assertions on how or when a function was called.
A classic situation where mocking comes to help is when a function involves some interactions with a database: it would be impractical and outside the scope of a unit test to verify that the data retrieved from the database is correct. For this reason, the call is usually stubbed and/or spied on.
We have a whole blog article just about mocking, you can read it here: What is mocking?
- What are some common misconceptions about unit testing?
Unit tests are useless.
Many software engineers dismiss the importance of unit testing because they don’t appreciate the large impact they can have on the health of their software, and because their time consuming nature is a natural deterrent. The truth is that, no matter the size or maturity of a project, unit tests are one of the easiest ways to guarantee robust, professional code.
Unit tests are time consuming.
Software engineers can spend up to 35% of their workweek on testing. But tools to automate and speed up the process are now available on the market. Give Ponicode a go for free.
Unit tests are difficult to create.
While it’s true that the syntax of test frameworks can be hard to grasp, there are tools out there which simplify and greatly ease the process of writing exhaustive unit tests. Again, you can try Ponicode for free and see for yourself how simple it is to implement.
And there is more! Here are the questions we answered in our previous blog posts
- What is Test Driven Development? (and how can you follow TDD in the Ponicode environment?)
- What is the best testing framework for me? Where we introduce the pros and cons of Jest and Mocha