Skip to content

Lesson 05 – Cyclomatic Complexity and Unit Testing

Complex code is harder to test and maintain. Cyclomatic complexity (CC) is a metric that counts the number of independent paths through your code. This lesson explores how CC is calculated and why it matters for unit testing.


1. What is Cyclomatic Complexity?

Cyclomatic complexity measures the number of decision points in a function or method. Each if, for, while, or logical operator adds branches that increase the number of possible execution paths. The formula is typically the number of branches plus one.

A higher value indicates more potential paths to test and greater risk for hidden bugs.


2. Calculating Complexity

Consider the following Python function:

def login(user_type: str, is_active: bool, retries: int) -> str:
    """Return an action message based on user type and status.

    This function chooses a login message for either admins or guests and denies
    access to inactive users after too many retries.

    Args:
        user_type (str): Either "admin" or "guest".
        is_active (bool): Whether the user account is active.
        retries (int): Number of login attempts so far.

    Returns:
        str: The message displayed to the user.

    Raises:
        ValueError: If `user_type` is not recognized.

    Examples:
        >>> login("admin", True, 0)
        'Welcome admin'
    """
    if user_type not in {"admin", "guest"}:
        raise ValueError("Unknown user type")

    if not is_active:
        return "Account inactive"

    if retries > 3:
        return "Too many attempts"

    if user_type == "admin":
        return "Welcome admin"
    return "Welcome guest"

This function contains four decision points, resulting in a cyclomatic complexity of 5 (four branches plus one).

A simple flow diagram illustrates the branching:

flowchart TD
    A[Start] --> B{User type?}
    B -->|Admin| C[Admin path]
    B -->|Guest| D[Guest path]
    C --> E{Active?}
    D --> E
    E -->|No| F[Inactive]
    E -->|Yes| G{Retries > 3?}
    G -->|Yes| H[Too many attempts]
    G -->|No| I[Welcome]

3. Impact on Unit Testing

Each additional branch requires at least one test case to validate. As complexity grows, the number of tests needed for full coverage increases dramatically. Functions with a CC over 10 often become fragile and difficult to test thoroughly.

Keeping CC low helps you:

  • Write fewer, clearer tests
  • Reason about your code more easily
  • Reduce the risk of untested edge cases

4. Managing Complexity

Break up large functions into smaller ones, apply early returns, and simplify nested conditionals. Tools like radon can report CC values so you can track improvements over time.


Cyclomatic complexity isn’t just an academic metric—it directly affects the effort required to create reliable unit tests. Aim for simple, focused functions whenever possible.

Next, you'll survey different testing types to choose the right level of coverage for code of varying complexity.


Next up: Lesson 06 – Testing Types