Introduction
At the core of delivering quality software, the ability to measure the software’s effectiveness through metrics like code coverage is of great importance. Code Coverage provides an essential measure of the completeness of testing, allowing us to understand the areas of the codebase that require more attention. In that regard, as we strive to build high-quality software, code coverage becomes an essential tool for us to assess the quality and completeness of our testing efforts.
The main purpose of code coverage tests is to discover what part of the codebase is executed during testing and which is not. These code coverage metrics then allow developers to debug the software faster and ensure all tests are working as expected. Besides faster code generation, code coverage has other advantages, as discussed in this article.
In this article, we will discuss the common metrics of code coverage testing, which include:
- Functions or methods coverage metrics
- Condition coverage metrics
- Path coverage metrics
- Statement coverage metrics
- Branch coverage metrics
- Decision coverage testing metrics
- Finite state machine metrics
For the completeness of code coverage to be ensured, optimizing unit test coverage percentage and code coverage test percentage is needed to allow developers to increase the speed and quality of the written codebase. Optimizing code coverage is an important part of delivering high-quality software. While there is no one-size-fits-all ideal percentage, striving for high code coverage is a goal we should all aspire to. It is an essential tool for developers to assess the effectiveness of their testing efforts, allowing them to identify areas of code that may require additional attention. However, achieving high code coverage is not a guarantee of quality software. It’s, therefore, crucial to balance coverage with other measures of code quality like functional specification tests and user requirements specification tests. At the end of the day, the goal should be to achieve a high level of code coverage while ensuring that testing efforts are efficient and effective in identifying defects and ensuring software dependability.
Code Coverage Metrics
Code coverage is one of the many software testing metrics that assist in assessing the performance and quality aspects of software in development and production. At a high level, Code coverage Percentage is used to describe the percentage of lines of code of software that is executed during testing. It is tested using the following general formula:
Code Coverage Percentage = (Number of lines of code executed by a testing algorithm/Total number of lines of code in a system component) * 100
There are several ways to measure code coverage, but the most common approach is to use a code coverage tool like coverage tool in python. A code coverage tool is a software program that analyzes the codebase of an application to collect data on which lines of code are executed during testing.
Usually, code coverage tools are used in conjunction with automatic test code generation tools like qodo. qodo automatically analyzes a given source code and creates relevant tests to catch bugs before software deployment. qodo’s test generation tool has a VSCode extension (and an upcoming extension for JetBrains IDEs) developed with Python that auto-generates unit tests for your code to ensure a high percentage of python code coverage testing. At other times, code coverage can be measured manually by analyzing test cases and identifying which lines of code were executed during testing. However, this approach is very overwhelming and error-prone than using an automatic code coverage tool.
The main code coverage metrics are more like a family of code testing dimensions and concepts rather than a single straightforward formula and include:
1. Functions/Methods Coverage Metric
This is a metric of code coverage that specifically focuses on the number of functions or methods within a software’s codebase that are executed during testing. The aim of this metric is to ensure that each individual function or method in the code is executed at least once during code testing. Once the function is tested, any potential bug is flagged and addressed to ensure it is logically and functionally correct. Once the code coverage tool has been run, it generates a report that provides information on the percentage of the functions that were executed during testing.
For example, in the given script:
def coordinate_sum(x, y, z): sum = 0 if x > 0: sum += x if y > 0: sum += y if z > 0: sum += z return sum def coordinate_product(x, y): product=50 if x*y > product: product = x*y else: product = 0 return product
Function coverage would test if both the coordinate_sum and coordinate_product functions are tested at least once.
2. Condition Coverage Metric
This is also termed as predictive coverage and is a white-box code coverage test that analyzes every possible Boolean condition in the codebase and determines if it has been tested or not. Boolean expressions are statements in software development that evaluate to either true or false. During code coverage testing, all conditions are tested both for their trueness and falseness to ensure all possible combinations of conditions are evaluated. Each Boolean condition is tested independently of the other, which allows for bugs to be easily caught. This also means in cases of nested Boolean conditions, all the sub-conditions are also tested independently to find any unexpected behaviors, therefore, creating a comprehensive condition code coverage test.
For example, in this code, there is one Boolean condition that tests if the product of x and y is greater than the product.
def coordinate_product(x, y): product=50 if x*y > product: product = x*y else: product = 0 return product
In this scenario, two tests need to be created, which result in true and false.
3. Path Coverage Metric
This is a methodical and sequential code coverage test that tests all the paths in the codebase. A code path is the execution of a given module from the entry point to the exit point and involves a sequence of statements of a module that are executed in a specific order.
Path Coverage = Number of Paths Covered/ Number of Total Paths
In the majority of code bases, especially for complex software, code paths can become unlimited very fast. This makes the path coverage metric a complex feat to achieve. This needs to be put into consideration so that only the most critical paths are tested first for a minimum viable product, and other tests are added as the software matures.
For example, given the following code:
def coordinate_sum(x, y, z): sum = 0 if x > 0: sum += x if y > 0: sum += y if z > 0: sum += z return sum
The execution of this code generates eight possible paths, and each needs to be tested individually for a complete path coverage metric.
- x > 0, y > 0, z > 0
- x > 0, y > 0, z<= 0
- x > 0, y <= 0, z > 0
- x > 0, y <= 0, z <= 0
- x <= 0, y > 0, z > 0
- x <= 0, y > 0, z <= 0
- x <= 0, y <= 0, z > 0
- x <= 0, y <= 0, z <= 0
4. Statement Coverage Metric.
Statement coverage testing metric is a white-box testing approach where every statement in a codebase is tested. This approach recommends that every line of code in the codebase should be executed at least once during testing. This metric is usually used as the shallow definition of code coverage testing and is measured using the expression:
Statement Coverage = (number of executed statements/ total number of statements) * 100
For example, given the code:
def coordinate_sum(x, y, z): sum = 0 if x > 0: sum += x if y > 0: sum += y if z > 0: sum += z return sum def coordinate_product(x, y): product=50 if x*y > product: product = x*y else: product = 0 return product
Statement coverage metric would measure how many of the total lines of code are executed during testing.
5. Branch Coverage Metric
Branch coverage is a white-box code coverage testing metric that measures the extent to which every possible branch in the code is executed. A branch in code testing is a point in the code where the program execution can take one or more paths, especially in if and for loops. Branch coverage ensures that each decision from every branch is executed at least once. Branch Coverage tests both the conditional and unconditional branches. Branch coverage is expressed as:
Branch Coverage = Number of Executed Branches / Total Number of Branches
For example, given the following code:
def print_product(x, y): product=50 if x*y > product: product = x*y print(product)
Complete branch coverage test would test 3 branches as shown:
6. Decision Coverage Testing Metric
Decision coverage metric is closely related to branch coverage testing. While branch coverage deals with all branches, decision coverage deals with only the conditional branches.
7. Finite State Machine coverage metric
A Finite State Machine (FSM) is a mathematical computational model that is used to describe the behavior of a system by defining a finite number of states and transitions. In software development, FSM is used to model systems like control systems and communication protocols. Finite State Machine coverage testing is a metric measured during code coverage to test the extent to which states and transitions of an FSM have been executed during testing.
Conclusion
In conclusion, code coverage testing is a dynamic process that covers multiple aspects of a code base to ensure high-quality and reliable software. Each of the code coverage metrics brings a unique perspective into the code, each with its own strengths.
- Statement Coverage guarantees every line of code is executed at least once during testing.
- Methods Coverage ensures all methods and functions are executed at least once during testing.
- Condition coverage ensures all Boolean evaluations are executed at least once independently during testing.
- Branch coverage ensures that all conditional and unconditional branches in the code are executed during testing.
- Decision coverage ensures that all conditional branches in the code are executed during testing.
- FSM metric ensures all states and transitions of software code are measured.
- Path Coverage ensures all sequential processes of the codebase paths are measured.
It should be noted that no testing method or metric can guarantee perfect results. One should therefore aspire to combine the various metrics to create a comprehensive report that will be used as a guide for developers to develop more comprehensive multiple condition coverage and unit test coverage percentages. To generate a wide range of tests that encompass all the code coverage metrics, leverage the use of automated code testing generators like qodo, as shown in the video link here (Practicing Python Programming w/ Copilot & qodo – YouTube ), which will help to make your testing journey easier and faster.