1. Introduction

Tasting an apple is different from tasting an apple pie. Although the apple is still the main ingredient, an apple pie integrates several other elements that create a new and complex flavor. Thus, evaluating an apple is examining a raw ingredient, while evaluating a pie includes analyzing the processes applied to the apple, the pastry, and the cream, among others.

We can use the previously presented intuitive example to distinguish what unit and integration tests are in computing: the prior focuses on core components in a source code, and the latter takes care of derivate and complex elements of the source code.

In this tutorial, we’ll dive deep into unit and integration tests. Initially, we’ll have a brief introduction to what testing is in the context of computing and programming. Then, we’ll investigate the concepts and techniques of both unit and integration tests. Finally, we’ll compare these testing categories in a systematic summary.

2. Tests: What Are They?

Generally, tests are routines and strategies applied to software to check if it accomplishes a set of predefined requirements and is defect free. Thus, the central purpose of testing is to identify potential errors or incompatibilities between the specification of a software program and its implementation.

High-level benefits of testing are:

  • Enable a cost-effective process: testing helps to save money in the long term; they can detect multiple potential problems before software releasing, which makes correcting it much easier and cheaper
  • Improve the product quality: by checking if all the requirements (functional and non-functional) are being satisfied, tests secure a high-quality final software product
  • Improve customer satisfaction: particular tests, such as UI/UX tests, aims to improve customer satisfaction by providing the best experience possible to them

There are three general categories of tests: functional, non-functional, and maintenance. In addition, these categories have subcategories encompassing several testing techniques. In particular, we’ll explore unit and integration tests that belong to the functional testing category.

3. Unit Testing

In short, we can say unit tests are responsible for checking a small code piece representing a single unit of work. Thus, we aim to determine if a fragment of the software tested executes as expected. But, the main challenge here is understanding what a single unit of work means in this scenario.

We can see a unit of work as well-delimited operations that do not depend on other implemented modules or libraries. So, code units are in complete isolation from other ones. In a strict definition, we have units isolated from everything else in a source code, including core native libraries of the employed programming language.

Examples of such native libraries are string, math, and statistics operations provided together with the language but not natively loaded in its core.

But, using the presented strict definition makes it too hard to fragment a program into units. In such a way, it is common to define a unit as a heavily isolated component.

Heavily isolated components can use deterministic resources of the used programming language or main framework. So, a unit does not employ any third-party non-native code or code implemented in the project on tests (such as classes, methods, etc.).

The following figure depicts the concept of units in our scenario:

Unit

Unit tests are white-box tests, typically executed by the developers of software. Furthermore, we can perform them at any stage of software development.

Since unit tests focus on a small code piece, they have fast execution, and the results can be easily processed, thus enabling the programmers to find and correct errors efficiently. Moreover, since units are usually reused several times in a source code, they also avoid spreading errors in the software.

Finally, unit tests are cost-effective to maintain and update as necessary.

4. Integration Testing

While unit tests work with isolated pieces of code, integration ones are responsible for testing non-isolated fragments of software. The main idea is to evaluate how multiple units or modules communicate and work together in a single block of code.

So, we can summarize integration testing as the analysis of the interface between different units. The figure next exemplifies a scenario in which integration tests are applicable:

Integration

In such a way, to execute integration test routines, we first have to guarantee that the units are working as expected. Due to that, we assume that integration tests occur after the execution of all the available unit tests.

These presented characteristics make integration tests typically more complex than unit ones and much slower to execute. Thus, these tests are carried out not by the programming team but by a separate testing team.

In this scenario, testers commonly do not know the implementation details of the integrated units. This situation classifies, in general, integration tests as black-box tests. So, after executing the test routines, the testing team should report the results to the programmers asking for adaptions or improvements in the code.

Finally, errors found in integration tests may be hard to interpret. Defining a particular and precise technical incompatibility that harms the cooperation between units is challenging. Moreover, small changes in a unit’s operational behavior can cause cascading changes in code blocks that use it.

All these phenomena make integration tests typically more costly and hard to maintain than unit tests.

5. Systematic Summary

Testing is a crucial part of the software development process. Through executing tests batch, developers can determine if pieces of code, or even the entire program, work as expected and satisfy a set of previously defined requirements.

Moreover, there exist multiple and different categories and subcategories of tests. Among these categories, there are the functional tests that, among others, encompass the subcategories of unit and integration tests.

Unit tests work with pieces of code on isolation (complete isolation, by definition, and heavy isolation, technically). Thus, these tests focus on checking the operational behavior of code blocks with no dependencies.

Integration tests, in turn, are dedicated to analyzing pieces of code with integrated units. So, they, in summary, move efforts to check if the interfaces between units are working well.

The following table shows and compares relevant characteristics of unit and integration tests:

Unit Testing

Integration Testing

Works with

Pieces of code on isolation

Non-isolated pieces of code

Executed by

Programmers

Testing team

Error detection

Typically easy

Typically more difficult

Class

White-box

Black-box

Maintenance

Easy to maintain and update

Hard to maintain and update

Costs

Extremely cost-effective

Usually more expensive

6. Conclusion

In this article, we studied unit and integration testing. First, we explored general concepts of testing in the software development process. So, we in-depth analyzed the characteristics of unit and integration tests. Finally, we compared such characteristics in a systematic summary.

We can conclude that executing tests is critical to guarantee the correct operational behavior of software in development. Unit and integration tests have different objectives and are performed by heterogeneous teams. However, both unit and integration tests are essential for the holistic assessment of the operation routines of a software program.