Code Metrics: Cyclomatic Complexity and Unit Tests
R. Kevin ColeApril 21, 2007

Cyclomatic Complexity

Cyclomatic complexity is a static software metric pioneered in the 1970s by Thomas McCabe. The cyclomatic complexity number (CCN) is a measure of how many paths there are through a method. It serves as a rough measure of code complexity and as a count of the minimum number of test cases that are required to achieve full code-coverage of the method.

The equation for calculating the cyclomatic complexity number comes from the the theory of graphs where it refers to the number of paths from any point in a topological space to any other point. It is expressed as:

    CCN = E - N + P

where E represents the number of edges on the graph, N the number of nodes on the graph, and P the number of connected components. In programming terms, E represents the code executed as a result of a decision, N is the number of decision points (conditional statements) and P is the number of ways to exit the program. As a software metric it can be expressed as

    CCN = "The number of decision points" + 1

Measuring Complexity

Cyclomatic complexity measures the number of execution paths through a method; therefore, every method has, at a minimum, a cyclomatic complexity of 1 since there is at least one path through the method. This means that even the simplest getter/setter method has CCN = 1:

    public String getName()
{
return this.name;
}

In the following method there are two decision points. Remembering that every method has at least a CCN value of 1, the final value for the cyclomatic complexity of the getResult(...) method is 3.

    public int getResult(int p1, int p2) 
{
int result = 0;
if (p1 == 0)
{
result += 1;
} else
{
result += 2;
}
if (p2 == 0)
{
result += 3;
} else
{
result += 4;
}
return result;
}

Conditionals and loops add to the complexity of a method. Each additional if, case, while, etc, adds 1 to your CCN score because you're adding another potential path through the method.

In general,

  • add 1 for each if statement.
  • add 1 for each for statement.
  • add 1 for each while loop
  • add 1 for each do-while loop.
  • add 1 for each && (an implied if statement).
  • add 1 for each || (an implied if statement).
  • add 1 for each ? (an implied if statement).
  • add 1 for each . (an implied if statement).
  • add 1 for each case statement.
  • add 1 for each default statement.
  • add 1 for each catch statement.
  • add 1 for each finaly statement.
  • add 1 for each continue statement.

You'll notice that I keep referring to the CCN value of methods. Measuring the complexity of an entire class is somewhat meaningless. If a class has a dozen public attributes with the standard accessor methods it will have a CCN value of at least 24! Does that mean the class is complex? Of course not. A CCN value of 24 for a class doesn't mean much; a value of 24 for a method may mean you've got a plate of spaghetti to unravel (of course it could also indicate a large but trivial case statement).

There are several good tools available for calculating complexity. On various projects I've used:

  • JavaNCSS - also counts non-comment source statements and calculates CCN
  • PMD - also checks for coding standard violations.
  • JDepend - also calculates design quality metrics and package dependencies
  • Complexian - also calculates NPATH number of acyclic execution PATHs.

Cyclomatic Complexity and Test Coverage

The number of execution paths through a method is directly related to the understandability, maintainability, and testability of the method. A general rule of thumb states that in order to ensure a high level of test coverage, the number of test cases for a method should be at least equal to the method's cyclomatic complexity value. When the number of test cases is equal to the CCN value, you can feel confident that your tests have followed every path through your code.

The getResult(...) method above would require at least four test cases to achieve complete code coverage. The following table illustrates the argument values required to test each path through the method.

Test #p1p2
100
20not 0
3not 00
4not 0not 0

Another rule of thumb when considering the CCN value is the relationship between CCN and risk:

Cyclomatic ComplexityRisk Summary
1-10Simple, low risk
11-20Moderate complexity, medium risk
21-50Complex, high risk
51+Very high risk

Studies have shown that bug counts spike in methods with CCN > 13. A method with a CCN value greater than 10 is considered complex. By determining the cyclomatic complexity of various class methods and paying attention to outlier values, one can uncover code that may be a candidate for a testing and/or refactoring effort. The higher the value of CCN for a given method, the harder it is to test.

What To Do About High Complexity

There is a strong correlation between CCN value and defect density. When the determination has been made that a class is complex there are two steps available to mitigate the associated risk:

  • increase the number of unit tests
  • refactor the offending code into smaller pieces to reduce its complexity. This spreads the complexity over smaller, more manageable and therefore more testable methods

Software systems that have gone through a refactoring process with the goal of reducing complexity have higher quality and are more maintainable.

Drawbacks of Cyclomatic Complexity as a Metric

CCN is a useful aide in flagging possible problems in the code base but the number does suffer some potential drawbacks:

  1. Cyclomatic complexity is a measure of a program's structural complexity and not its data complexity.
  2. The same weight is assigned to nested and non-nested loops. (deeply nested conditional structures are harder to understand).
  3. It must be used with care as it may give a misleading figure with regard to frequency of simple comparisons and decision structures. A simple case statement can brand a method with a high CCN value.