Unit Testing in C Part 2 – Code Coverage

This is the series on Unit testing in C for embedded development. The aim of this series is to provide easy and practical examples that anyone can understand. This is the Unit testing in C Part 2 – Code coverage in unit testing.

Unit testing in C – Code Coverage

Introduction

Code coverage measures the number of lines of source code executed during a given test suite for a program. Tools that measure code coverage normally express this metric as a percentage.

Code\;Coverage= \left[\frac{Number\;of\;lines\;of\;code\;exercised}{Total\;Number\;of\;lines\;of\;code}\right]\times100\%

So, if you have 90% code coverage then it means, there is 10% of code that is not covered under tests. Code coverage tools will use one or more criteria to determine how your code was exercised or not during the execution of your test suite. Code Coverage utilities hook into your source code and your test suite and return statistics on how much of your code is actually covered by your tests.

The common metrics that you might see mentioned in your coverage reports include:

We will see one by one below.

Statement Coverage

This is a metric that ensures that each statement of the code is executed at least once. It measures the number of lines executed. It helps to check the do’s and don’t’s of source code.

Example

Let’s take this source. Now will assume that I am going to write one test case and passing the argument true to that test_func. So that code will execute like below.

void test_func( bool condition )
{
  if ( condition == true )
  {
    printf(“Condition is true\n”);
  }
else
{
printf(“Condition is false\n”);
}
}

So here, the colored lines will execute. That means, 7 lines will be executed out of 11 lines. So the else part is not at all covered. If we want to cover the else part, we cannot achieve that using one test case. Because of that branch (if-else), it will execute only one path. So we have to write another test to cover that else part.

I am writing the 2nd test case to test the else part. This time I have to pass false to the argument condition. In that case, it will execute the statements below.

void test_func( bool condition )
{
  if ( condition == true )
{
printf(“Condition is true\n”);
}
else
  {
    printf(“Condition is false\n”);
  }
}

So, the 2nd test case will run through 8 statements out of 11 statements. In this case, it is not running if part. So if we combine both the test cases, we will cover 100% statements of this code.

What is the use of statement coverage?

  • We can find the dead codes
  • We can find the unused branches

Decision or Branch Coverage

Branch coverage ensures each branch in the program (e.g., if statements, loops) have been executed. That means each branch has been executed at least once during testing. So each branch condition must have been true at least once and false at least once during testing. Confused? Lol Sorry for that bad explanation. Will clear that by the below example. We can use the same example which we have used above.

Example

Let’s take this source. Now we will assume that I am going to write one test case and passing the argument true to that test_func. So that code will execute if a condition like below.

void test_func( bool condition )
{
if ( condition == true )
  {
    printf(“Condition is true\n”);
  }
else
{
printf(“Condition is false\n”);
}
}

So here, the colored lines will execute (branch). We have covered one path of the branch. But still, another path is there to test which else part.

I am writing the 2nd test case to test else part. This time I have to pass false to the argument condition. In that case, it will execute the statements below.

void test_func( bool condition )
{
if ( condition == true )
{
printf(“Condition is true\n”);
}
else
  {
    printf(“Condition is false\n”);
  }
}

So, the 2nd test case will run through the else part. In this case, it is not running if part. So, if we combine both the test cases, we will cover 100% branches of this code (We have covered both if and else part).

What? Just wait a minute. I have explained both the statement coverage and the branch coverage with the same example and same test cases. So, Does that mean, if we cover all the statements it will cover all the branches as well? In the above example YES. But if you see other examples it is not true.

Difference between statement coverage and branch coverage

Let’s discuss another example given below.

For the above example, I am writing one test case. In that test case, I am passing the argument as true (condition = true). In such a case, the statement coverage is like below.

void test_func( bool condition ) 
{
  if ( condition == true )
  {
     printf(“Condition is true\n”); 
  }   
  printf(“EmbeTronicX\n”);
}

The colored lines will be executed when condition is true. That means, we have covered 100% statement with only one test case.

So now we will come to our question. Here we have achieved 100% statement coverage. Does that mean, we have achieved branch coverage also? Obviously NO. Why and How? See the below image. I have put the path of the code execution.

branch-coverage-vs-statement-coverage-unit-testing-in-c-code-coverage

So by using the above picture, We have not covered the red line path which is false case of if(). But we have covered 100% of the statement coverage and missed the one path of the branch. So branch coverage will differ from statement coverage when branches are “empty”. In the above example, else part is missing. So the branch is empty here.

If we achieve 100% of branch coverage, that means we have covered all the statements too. But if we achieve 100% of statement coverage, that doesn’t mean, we have covered all the branches as well.

Note:

100% branch coverage = 100% statement coverage

100% statement coverage != 100% branch coverage

Condition Coverage

Condition coverage only applies to logical operands like AND, OR, XOR. In order to ensure complete Condition coverage criteria, the logical operands should be evaluated at least once against “true” and “false“. This is closely related to decision coverage but has better sensitivity to the control flow. However, full condition coverage does not guarantee full decision coverage. Condition Coverage is also known as ‘Predicate Coverage’.

Let us take an example to explain Condition Coverage

In order to suffice valid condition coverage for this pseudo-code following tests will be sufficient.

TEST 1: X=TRUE, Y=FALSE
TEST 2: X=FALSE, Y=TRUE

Modified condition/decision coverage (MCDC)

The modified condition/decision coverage (MC/DC) coverage is like condition coverage, but every condition in a decision must be tested independently to reach full coverage. This means that each condition must be executed twice, with the results true and false, but with no difference in the truth values of all other conditions in the decision. In addition, it needs to be shown that each condition independently affects the decision.

If you want to understand clearly the MC/DC, please see this video. (credits: udacity)

Function Coverage

Function Coverage refers to the number of functions in your code that were tested.

Function call coverage

It is a very common scenario in programming that one function calls another and so on. There is a calling function and a called function. So this coverage technique ensures that there do not exist any faults in the function call.

Line Coverage

Line Coverage is straightforward. It is the number of lines of code your tests evaluated.

Loop Coverage

This technique is used to ensure that all the loops have been executed, and the number of times they have been executed. The purpose of this coverage technique is to make sure that the loops adhere to the conditions as prescribed and don’t iterate infinitely or terminate abnormally. Loop testing aims at monitoring the beginning until the end of the loop.

Keep this in your mind

Just remember one thing, having “100% code-coverage” doesn’t mean that everything is tested completely and doesn’t mean that they are tested under every (common) situation, while it means every line of code is tested but not on the real situation.The coverage doesn’t reflect the code quality, it just tells you how many lines are covered by a test. A piece of code with a coverage of 100% could have as many bugs as code without the tests. I would use code-coverage to highlight bits of code that I should probably write tests for. Basically, 100% code-coverage doesn’t mean your code is perfect. Use it as a guide to writing more comprehensive unit tests.

How should I deal with the coverage?

Now you know what code coverage isn’t you probably think, so why should I use it then? Code coverage helps you and your development team, for example, it requires every developer to do the minimal effort of testing.

It also helps you to be a better developer. When you write your own code and you know you have to test it you’ll notice that your code will be more clean and easy to understand to make it easier to test.

Final words

Code coverage is especially important with Test Driven Development, where the developer writes his tests before he writes his code. TDD is meant to inform the Agile development process and help developers write cleaner code with fewer lines of junk. In this case, Code Coverage helps developers write better tests, and helps keep their code on target by pointing out code that falls outside the expected development scope

In our next tutorial, we will see how to install the unit test framework.

0 0 vote
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
%d bloggers like this: