What the heck is cyclomatic complexity?

According to the SEI, “Cyclomatic complexity is the most widely used member of a class of static software metrics. Cyclomatic complexity may be considered a broad measure of soundness and confidence for a program. Introduced by Thomas McCabe in 1976, it measures the number of linearly-independent paths through a program module.”

Technically, cyclomatic complexity is calculated by the number of edges, less the number of nodes plus the number of connected components of a control flow graph of the program being measured. (Aren’t you glad you asked!)

Ok, that’s fine, but how does that relate to testing?

Interestingly, the complexity measure of a method is also the exact number of tests that is needed to achieve 100% path coverage through an application. The measure of complexity also is a great indicator of the testability, or “de-testability” of a particular method. The SEI present the following table as a basis for common complexity threshold values.

Cyclomatic Complexity Risk
1-10 A simple, low risk program
11-20 moderate complexity, risk complexity
21-50 Complex, high risk
Greater than 50 high risk, detestable program

Structured basis testing is a testing approach based on cyclomatic complexity. Simplistically, to build tests based on complexity, you should start at the first line of a method, and count the straight through path as 1, that is your first test. Then, manually calculate the complexity for the method and as you go, add a corresponding test each time you add to the complexity count.

An example

In The art of software testing, Glenford Myers uses a simple self assessment test, for the reader to work out how many test cases you need for a simple program. The program in his example accepts three integers, one for each side of a triangle, and determines whether the triange is scalene, isosceles or equilateral.

string CheckTriangle (int SideA, int SideB, int SideC)
In case you were wondering, A scalene triangle is one where no two sides are equal, an equilateral triangle is one where all the sides are equal and an isosceles triangle has two equal sides.

My implementation of the CheckTriangle method, results in a simple, yet quite complex piece of code, which has a cyclomatic complexity of 12.

 1  public static string CheckTriangle (int a, int b, int c)
 2  {
 3      if (a <= 0 || b <= 0 || c <= 0)
 4      {
 5         return "Illegal Triangle";
 6      }
 7
 8      if ((a > b + c || b > a + c || c > a + b))
 9      {
10         return "Illegal Triangle";
11      }
12
13      if (a == b && b == c)
14      {
15         return "Equilateral Triangle";
16      }
17
18      if (a == b || b == c || a == c)
19      {
20         return "Isosceles Triangle";
21      }
22
23      return "Scalene Triangle";
24  }
So how did I calculate the complexity?

There are several free tools on the market for .net developers to calculate the cyclomatic complexity of a given piece of code, including Sourcemonitor, Reflector with the Codemetrics add-in developed by Jonathan De Halleux, and CodeRush with the metrics add-in.

Manually calculating the complexity, and writing tests as we go.

You can also calculate complexity manually quite easily, for example:

Code Complexity
Straight through path 1
if (a <= 0 || b <= 0 || c <= 0) 3
if ((a > b + c || b > a + c || c > a + b)) 3
if (a == b && b == c) 2
if (a == b || b == c || a == c) 3
Total Complexity 12

Ok so I know the complexity, how many tests do I need?

To achieve 100% statement coverage, we need the following test cases:

Test # Description Test Data
1 A valid scalene triangle. a = 2, b = 3, c = 4
2 An illegal triangle with a zero length side. a = 0, b = 1, c = 1
3 An illegal triangle with one short side. a = 1, b = 4, c = 2
4 A valid equilateral triangle. a = 1, b = 1, c = 1
5 A valid isosceles triangle. a = 1, b = 2, c = 2

That’s where most developers would stop, highlighting again why developers should never test their own code. We need some additional cases to ensure that we cover all the conditionals, so let’s add to and the following tests.

Test # Description Test Data
6 An illegal triangle where side b is zero length. a = 1, b = 0, c = 1
7 An illegal triangle where side c is zero length. a = 1, b = 1, c = 0
8 An illegal triangle where side b is too short. a = 2, b = 1, c = 4
9 An illegal triangle where side c is too short. a = 4, b = 2, c = 1
10 An isosceles triangle where the short side is b. a = 2, b = 1, c = 2
11 An isosceles triangle where the short side is c. a = 2, b = 2, c = 1
12 An equilateral triangle to test the right side of the && in line 13, i.e. b == c a = 1, b = 1, c = 1

Ok so we now have 12 tests that cover all possible paths through the application, however we do have some duplication, like test 12, and some of the descriptions are a bit messy, so let’s refactor.

Test # Description Test Data
1 A valid scalene triangle. a = 2, b = 3, c = 4
2 An illegal triangle where side a is zero length. a = 0, b = 1, c = 1
3 An illegal triangle where side b is zero length. a = 1, b = 0, c = 1
4 An illegal triangle where side c is zero length. a = 1, b = 1, c = 0
5 An illegal triangle where side a is too short. a = 1, b = 2, c = 4
6 An illegal triangle where side b is too short. a = 2, b = 1, c = 4
7 An illegal triangle where side c is too short. a = 4, b = 2, c = 1
9 A valid equilateral triangle. a = 1, b = 1, c = 1
9 An isosceles triangle where the short side is a. a = 1, b = 2, c = 2
10 An isosceles triangle where the short side is b. a = 2, b = 1, c = 2
11 An isosceles triangle where the short side is c. a = 2, b = 2, c = 1

We now have a set of tests that has 100% path coverage, but are we done? Well the tester in me still can think of a couple of tests to add to cover the boundary conditions on line 3.

Test # Description Test Data
12 An illegal triangle where side a is negative. a = -1, b = 2, c = 2
13 An illegal triangle where side b is negative. a = 2, b = -1, c = 2
14 An illegal triangle where side c is negative. a = 2, b = 2, c = -1

Like most testing techniques using cyclomatic complexity is not a testing silver bullet that writes your test for you. It is however a useful technique for white box testing to guide you as to which tests need to be written for a specific function or method that has already been written. Now if only Visual Studio 2005 could programatically create these test cases for you when you generate the tests for an existing method.

4 thoughts on “What the heck is cyclomatic complexity?

  1. Gary,

    Whilst I am no math’s wizard, I believe that the information in my article is correct. The second condition tests that the sum of the length of every two sides must exceed the length of the third side. For example, sides of length 2, 1, 1 do not make up a valid triangle. You can test this out online here http://ostermiller.org/calc/triangle.html if you wish.

    Bruce

  2. I think Gary is right. I tried the link you gave for testing triangle validity. The lengths choosen by me were a=3, b=2, c=1. The output I received was :-

    “The values entered cannot make a triangle. The sum of the length of every two sides must exceed the length of the third side.”

    It says “sum of two sides must exceed the length of third side”. So that means even if they are equal it is invalid triangle. In the above case, a = b + c and it is invalid triangle. However, your test condition fails.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>