Definition of Unit Test
In computer programming, unit testing is the procedure used to ensure that software or source code (or part of software or part of code) functions correctly.
Having long been considered a secondary task, unit testing has become a common and even essential practice, now an integral part of the standard development lifecycle.
Unit testing is an essential part of any source code to ensure that the application works as intended in the specification, despite any changes to the source code. In addition, good software development practices such as Test Driven Development (TDD) and DevOps rely heavily on unit testing.
The role and criteria of unit testing
Unit tests are developed to compare the final version delivered with the specification normally provided in the early phases of an IT project. They are used to determine whether or not a verification has been successful. A test can therefore be understood as a criterion for validating the expectations of a programme or computer code.
Mike Cohn, one of the main contributors to the Agile Scrum methodology, places unit tests at the base of his Test Pyramid (in which the next levels correspond to service and interface tests).
Among other things, unit testing makes it possible to
Quickly identify errors:
- Several methods (including the eXtreme Programming method) recommend writing tests at the same time as, or even before, the function to be tested, to facilitate debugging.
Facilitate maintenance:
- After a code modification, tests can be used to quickly identify any regressions (failed tests).
Complete code documentation:
- Tests can often be used to highlight the use of a function. In this way, they can be used to supplement feature documentation.
A number of criteria must be met in order to properly define a unit test:
- Unit: Unit testing focuses on the smallest identifiable element of an application (referred to as a unit). However, several elements or lines of code can be used to define a unit, which can then be a function, a class method, a module, an object, etc. This is why unit tests are called low-level tests (as opposed to high-level tests, which check the validity of one or more more more complex functionalities).
- White-box testing: The test must be carried out in a white box to reflect the fact that the quality engineer often in charge of the test or the developer himself must have knowledge of the part of the code (of the unit) to be tested.
- Isolation: The tests must be carried out in isolation, because each unit tested must be independent of the other tests. The suite of unit tests must be able to be run in any order without affecting the results of the following or previous tests.
- Speed: the various tests must be able to run quickly, so that they can be launched whenever the source code is modified.
- Idempotence: idempotence means that an operation has the same effect each time it is applied. The test must therefore be independent of the environment or the number of times it is run.
- Automated: The unit test must be able to produce a direct result in terms of success or failure and not require manual intervention by the developer to conclude.
To sum up, we can say that the real value of testing is appreciated when major changes are made to the source code of an application or system. Testing then helps to ensure that the expected behaviour is maintained. Unit testing therefore helps to reduce the level of uncertainty.
While testing methodologies and frameworks are now well established in software development, how and where does unit testing fit in with data science?
Â
Â
💡Related articles:
Machine learning unit tests
In traditional software development, the key element that influences the behaviour of the system is the source code. Machine Learning technologies have introduced 2 new elements that also have a direct impact on the system’s behaviour. These are the model used and the data. Furthermore, the behaviour of Machine Learning models is not specifically defined by lines of code, but mainly by the data used in the training phase.
This is why systems based on Machine Learning technologies require additional testing, because the rules governing the behaviour of the overall system are less explicit than the rules of traditional systems.
![test unitaire](https://datascientest.com/wp-content/uploads/2022/05/schema_1.png)
Machine Learning work loops very often straddle 3 environments:
The research and development environment:
This is most often the Jupyter Notebook used to develop the various models and approaches. However, while it is technically possible to build and run unit tests on basic functionalities (of a class, for example, in Python), most unit tests are generally run in the development and production environments.
The development environment:
This is the environment in which most tests are carried out. There are generally several levels of testing in this environment:
- Unit tests, which form the basis (as illustrated in the diagram below)
- Integration tests to ensure that the various components function optimally
- System tests to ensure that the overall system functions correctly
![test unitaire](https://datascientest.com/wp-content/uploads/2022/05/schema_2.png)
The question then arises of where to position the cursor for defining the number of tests. To define the optimum number of tests, we can use the following questions:
- Does the test base make it possible to reduce uncertainty about the operation of our system?
- What are the critical and main tasks of our system?
- Are the priorities clearly identified?
Â
With regard to machine learning issues, examples of unit tests that can be carried out are given below:
- Testing the format and type of data
- Testing the parameters of the Machine Learning model
- Testing input variables
- Testing model quality
- The production environment: This is the environment in which users of an application will have access to it. In this environment, the tests carried out to ensure that the overall system is working properly are generally much more complex.
Unit Test libraries in Python
In Python, the two main libraries used to run unit tests are Unittest and Pytest. Using the unittest library to run tests requires a good knowledge of Python’s object-oriented programming, because the tests are run via a class.
In contrast, using the Pytest library offers a little more flexibility in the design and implementation of tests. In addition to the fact that running tests with the Pytest library is much more flexible, the Pytest library offers more test commands and options (assert instruction).
To illustrate simple use cases for these two libraries, we’ll look at the following two examples:
- Test of a function calculating the square of an integer: We will test that the values given as output are integers (test carried out with Pytest).
- Testing the type and ranges of input data (using unittest)
Although far from exhaustive, these two examples will give you a better idea of some of the ways in which these libraries can be used.
Example of a Unit test with Pytest
Suppose, for example, that as part of a project, developers have created a function to calculate the square of an integer, and we want to run tests to ensure that the function’s outputs are integers. So they decide to run tests on a list of values.
Step 1: Define the number_squared function in function.py
Step 2: Defining the test function in the test_in_int.py file
Executing the pytest command in a terminal returns the following result:
![test unitaire](https://datascientest.com/wp-content/uploads/2022/05/test-unitaire-ecran.png)
The only constraint imposed by the use of the Pytest library is that the name of the test file must begin with the prefix ‘test_’ (the full name would be test_file_name.py). In this way, whenever the function is modified or updated, the test function can be used to ensure that the expected behaviour of the function remains consistent.
Example of a test with unittest
In this example, we’re going to use the unittest library to run a test with an example dataset. We will use the iris dataset (click here for more details). The test we want to implement is to check the type and extent of the input data for a Machine Learning model.
Let’s imagine, for example, that the Data Scientist in a team has developed a model for classifying three species of flowers. However, the data (precisely the variables in our dataset) used to develop the model were of a certain type and spanned a certain interval.
If, by any chance, the information provided by users of our model were very different from the training data (known in English as data drift), the Machine Learning model put into production could no longer be relevant and provide inconsistent predictions. This is why it is necessary during the development phase to define a test to ensure that the input data is consistent with the training data.
From the overview and description of the dataset below, we can see that the variables are floats (real numbers) with values generally between 1 and 8.
![aperçu du jeu de données](https://datascientest.com/wp-content/uploads/2022/05/apercu-du-jeu-de-donnees.png)
![description du jeu de données](https://datascientest.com/wp-content/uploads/2022/05/Description-du-jeu-de-donnees.png)
Step 1: Definition of a dictionary of variable types and ranges
In a dictionary structure, we will define both the type and the range of each variable in line with the description of the dataset above (Analogy with the definition domain in mathematics).
Step 2: Creation of a data processing and training pipeline for a logistic regression model
This stage can be broken down into several phases, but we have grouped all the phases together in a single class.
Step 3: Defining the test class for our test campaign
With the unittest library, you need to define a test class:
Stage 4: Running the unit tests
To run the test inside a Jupyter Notebook, simply use the code below:
Running the code gives us the following display:
![test unitaire](https://datascientest.com/wp-content/uploads/2022/05/test-unitaire-final.png)
Conclusion about Unit Tests
Unit testing is an essential step in reducing uncertainty and ensuring consistency between the input specification and the final product. It’s easy to see why this stage is so important in agile methods.
In the development process for Machine Learning applications, unit tests are most often carried out during the development phase by a Data Scientist or Data Engineer to guarantee the system’s performance and ensure that the results obtained are consistent with the input data.
If you’re interested in becoming a Data Engineer, find out more about the career path designed by DataScientest on the dedicated page.