# Achieving 100% MC/DC coverage

# The MC/DC metric

The Modified condition/decision coverage (MC/DC) metric is one of the “big guns” among the coverage metrics, giving quite good guarantee the covered code is sufficiently tested. Its definition is quite succint (copied from Wikipedia):

- Each entry and exit point is invoked.
- Each decision takes every possible outcome.
- Each condition in a decision takes every possible outcome.
- Each condition in a decision is shown to independently affect the outcome of the decision.

# Condition vs. decision

Consider the following example (and ignore we can simplify the `if`

):

```
public static boolean doSomething(boolean a, boolean b, boolean c) {
if ((a || b) && c) {
return true;
} else {
return false;
}
}
```

Here our **decision** is `(a || b) && c`

. The **conditions** are the booleans that decision depends on: `a`

, `b`

and `c`

^{1}. Our job is to find how to cover that decision with test cases to guarantee it’s 100% bug-resistant (according to the definition above).

# The road to the 100%

The definition gives us a very good understanding *what* means to have 100% coverage, but not *how* to get there. Let’s focus on the 4 points in the definition. This will allow us to analyze getting to 100% with a minimal set of test cases.

*Covering entry/exit points*- That’s easy to achieve. Just write a test case which reaches each`return`

.*Covering every decision outcome*- Slightly harder, but still relatively easy - just make sure we get the two different results for each decision.*Covering each condition*- Make sure we’ve actually made all the conditions`true`

or`false`

. Easy again!^{2}*“Proving” the result depends on each condition*- That’s slightly harder, even when we’re not proving it in the mathematical sense.**This is what we’ll focus on.**

# Minimizing the test cases

OK, we figured out we need to prove the result of the decision is affected by the different conditions, but how to do it with a minimum amount of test cases? I mean less than `2^n`

^{3}?

We need *at least* `n+1`

test cases to cover point 4 of the MC/DC definition for a boolean decision^{4}. How are we going to find these test cases? Note there are *many* solutions to this problem.

# How to figure out the inputs?

I’ll sketch a procedure for informally finding out a set of test cases. This is not an algorithm in the strict sense, it just helps you sort the things out using your intuition.

In other words, this is not a 100% mathematically provable algorithm :)

# Step 1 - construct a truth table

Let’s construct a truth table for the `doSomething`

method:

Case | a | b | c | Decision |
---|---|---|---|---|

1 | false | false | false | false |

2 | false | false | true | false |

3 | false | true | false | false |

4 | false | true | true | true |

5 | true | false | false | false |

6 | true | false | true | true |

7 | true | true | true | false |

8 | true | true | true | true |

That are all the `2^n`

test cases we’d write to test all possible inputs. We’re trying to find a subset of them.

# Step 2 - select a test case

Let’s start with a random line, e.g. case 3:

Case | a | b | c | Decision |
---|---|---|---|---|

3 | false | true | false | false |

# Step 3 - find your next test case

Now we want to change a condition and verify it affects the decision. For example, changing `c`

to `true`

would change the result to `true`

(case 4). Add it to the table:

Case | a | b | c | Decision |
---|---|---|---|---|

3 | false | true | false | false |

4 | false | true | true | true |

Note: You should not necessarily always base the new test case on the last one you’ve added.

# Step 4 - repeat

Now repeat step 3 until you get `n+1`

test cases.

For example, we can now change `b`

to `false`

, changing the decision to `false`

(case 2):

Case | a | b | c | Decision |
---|---|---|---|---|

3 | false | true | false | false |

4 | false | true | true | true |

2 | false | false | true | false |

Now we have to verify this for `a`

too. Changing it to `true`

would change the decision to `true`

:

Case | a | b | c | Decision |
---|---|---|---|---|

3 | false | true | false | false |

4 | false | true | true | true |

2 | false | false | true | false |

6 | true | false | true | true |

# Profit!

Now we have a good set of test cases, ready to be turned into code:

Case | a | b | c | Decision |
---|---|---|---|---|

3 | false | true | false | false |

4 | false | true | true | true |

2 | false | false | true | false |

6 | true | false | true | true |

An indication we’re ready is that for each condition (`a`

,`b`

,`c`

) we have two different decision results for the two values of that condition.

Coincidentally, we have `n+1`

test cases, which means this is also a **minimal** set of tests! :)

# Footnotes

Of course, these 3 guys needn’t be booleans. They can be any expressions! But it’s easier to treat them as “dumb” booleans, to avoid messing up with too much syntax. ↩

Unless you have conditions which can never be true (e.g.

`1 == 2`

) or false (e.g.`1 == 1`

). Then it’s impossible. Sorry! ↩Covering all the

`2^n`

inputs will guarantee we’ve covered each theoretically possible outcomes of the decision. Not always the best idea, though. ↩Sketch for an induction proof: For 1 condition we obviously need 2 test cases to verify it affects the decision. Now assume we have

`n`

conditions covered by`n+1`

cases, “proving” these`n`

conditions affect the decision. Adding a new condition will require us to add at least one more test case - we can add one of the values (true/false) for the condition to the existing test cases (without affecting their decision verification), but in order to prove the new condition can change the decision we should add a new test case, ending up with`n+2`

cases. If we could go on and test the new condition without adding a new test case (just by modifying the existing ones), we’d surely “lose” a test case for another condition. ↩