Hone logo
Hone
Problems

Mastering MC/DC Coverage with Jest

This challenge focuses on implementing and verifying Modified Condition/Decision Coverage (MC/DC) for a given TypeScript function using Jest. MC/DC is a rigorous testing criterion that ensures each condition within a decision has been independently shown to affect the outcome of the decision. Achieving MC/DC is crucial for safety-critical systems and advanced software quality assurance.

Problem Description

Your task is to write a set of Jest tests for a provided TypeScript function that aims to achieve Modified Condition/Decision Coverage (MC/DC). You will need to:

  1. Understand the target function's logic: Analyze the function to identify all boolean conditions and the decisions they influence.
  2. Design test cases: Create specific input values that, when passed to the function, will satisfy the MC/DC criteria. This involves ensuring each condition is tested in both true and false states, and importantly, that each condition's change independently alters the outcome of the decision.
  3. Implement Jest tests: Write these test cases using Jest's assertion library.
  4. Verify MC/DC: Use Jest's coverage reports (specifically, a tool that can report MC/DC, such as nyc with appropriate configuration) to confirm that your test suite achieves 100% MC/DC for the target function.

The core difficulty lies in constructing test cases that isolate the effect of individual conditions on the overall decision outcome, which is the essence of MC/DC.

Examples

Let's consider a hypothetical function:

// targetFunction.ts
export function processInput(a: boolean, b: boolean, c: boolean): string {
  if ((a || b) && c) {
    return "Case 1: Both branches taken";
  } else if (a && !b) {
    return "Case 2: Only 'a' true, 'b' false";
  } else if (!a && b) {
    return "Case 3: Only 'a' false, 'b' true";
  } else {
    return "Case 4: Default";
  }
}

Example 1: Demonstrating "a || b" and "c" conditions

Input for processInput:

a = true, b = false, c = true

Output from processInput:

"Case 1: Both branches taken"

Explanation:

This input tests the first part of the if condition: (a || b) evaluates to (true || false) which is true. Then true && c (which is true && true) evaluates to true. The function enters the first block. To achieve MC/DC for a || b, we'd need tests where:

  • a = true, b = false (makes a || b true)
  • a = false, b = true (makes a || b true)
  • a = false, b = false (makes a || b false)

And for c, we'd need:

  • c = true (when a || b is true)
  • c = false (when a || b is true)

Example 2: Demonstrating "a && !b" condition

Input for processInput:

a = true, b = false, c = false

Output from processInput:

"Case 2: Only 'a' true, 'b' false"

Explanation:

The first if condition ((a || b) && c) evaluates to ((true || false) && false) which is (true && false), resulting in false. The code proceeds to the else if (a && !b). Here, a && !b evaluates to true && !false, which is true && true, resulting in true. The function enters the second block.

Example 3: Demonstrating "a" and "!b" independently

Input for processInput:

a = false, b = true, c = false

Output from processInput:

"Case 3: Only 'a' false, 'b' true"

Explanation:

The first if condition is false. The second else if (a && !b) evaluates to (false && !true), which is (false && false), resulting in false. The code proceeds to the next else if (!a && b). Here, !a && b evaluates to !false && true, which is true && true, resulting in true. The function enters the third block.

To achieve MC/DC for a && !b, we need to show that:

  • a being true affects the outcome when !b is true.
  • a being false affects the outcome when !b is true.
  • !b being true affects the outcome when a is true.
  • !b being false affects the outcome when a is true.

Constraints

  • The target function will be provided as a separate TypeScript file (e.g., targetFunction.ts).
  • Your solution must be a Jest test suite written in TypeScript.
  • The test suite must achieve 100% MC/DC coverage for the provided targetFunction.
  • You are expected to use a standard Jest setup with ts-jest or a similar transformer.
  • MC/DC reporting will be verified using the nyc (or istanbul) command-line tool, configured to report MC/DC coverage.

Notes

  • Achieving MC/DC requires careful consideration of how each condition contributes to the decision's outcome. A truth table can be a helpful tool for designing test cases.
  • For a decision like (A || B) && C, MC/DC requires testing:
    • Every condition is true and false independently.
    • Every condition independently causes the overall expression to be true.
    • Every condition independently causes the overall expression to be false.
  • Consider the order of evaluation and short-circuiting in boolean expressions.
  • You will need to install nyc (npm install --save-dev nyc) and configure it to report MC/DC. A common package.json configuration for nyc might include:
    "nyc": {
      "reporter": ["text", "html"],
      "branches": 90, // Or higher for MC/DC, though not strictly enforced by nyc itself
      "functions": 90,
      "lines": 90,
      "statements": 90,
      "per-file": true,
      "cache": true,
      "exclude": [
        "**/*.d.ts",
        "**/node_modules/**",
        "**/dist/**",
        "**/test/**"
      ],
      "check-coverage": true,
      "report-dir": "./coverage",
      "report-strategy": "branch", // Important for MC/DC
      "all": true // Ensure all files are included
    }
    
    And you would run your tests with npx nyc --reporter=lcov --reporter=text-on-lcov npm test. The "report-strategy": "branch" is a key hint for how nyc can be leveraged for branch coverage, which is foundational to MC/DC. You will need to interpret the coverage report to confirm 100% MC/DC.
Loading editor...
typescript