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:
- Understand the target function's logic: Analyze the function to identify all boolean conditions and the decisions they influence.
- 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
trueandfalsestates, and importantly, that each condition's change independently alters the outcome of the decision. - Implement Jest tests: Write these test cases using Jest's assertion library.
- Verify MC/DC: Use Jest's coverage reports (specifically, a tool that can report MC/DC, such as
nycwith 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(makesa || btrue)a = false, b = true(makesa || btrue)a = false, b = false(makesa || bfalse)
And for c, we'd need:
c = true(whena || bis true)c = false(whena || bis 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:
abeingtrueaffects the outcome when!bistrue.abeingfalseaffects the outcome when!bistrue.!bbeingtrueaffects the outcome whenaistrue.!bbeingfalseaffects the outcome whenaistrue.
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-jestor a similar transformer. - MC/DC reporting will be verified using the
nyc(oristanbul) 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 commonpackage.jsonconfiguration fornycmight include:
And you would run your tests with"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 }npx nyc --reporter=lcov --reporter=text-on-lcov npm test. The "report-strategy": "branch" is a key hint for hownyccan be leveraged for branch coverage, which is foundational to MC/DC. You will need to interpret the coverage report to confirm 100% MC/DC.