Composite assertions combine several conditions in one assertion. This rule raises an issue when a Node.js assert call or a Jest-like truthiness matcher combines independent conditions with &&, or asserts that every condition in an || expression is false.

The rule applies only to assertions in test code.

Why is this an issue?

When an assertion combines independent conditions, its failure does not identify which condition was not satisfied. Splitting the conditions into separate assertions produces a more precise failure and makes the problem easier to diagnose.

However, && is also commonly used to express one cohesive check on a single value. When one operand checks that a value exists, has a given type, or has a given shape, and every remaining operand only uses that same value, the conditions are not independent: the later operands rely on the earlier check. Such guard chains should remain a single assertion and are not reported:

import assert from 'node:assert/strict';

assert(primaryColumns[0] && primaryColumns[0].length > 0);
assert(typeof value === 'object' && value !== null);
assert(Array.isArray(value) && value.length > 0);
assert(isNonEmpty(list) && list[0].id);
assert(value instanceof Error && value.message);
assert('create' in rule && typeof rule.create === 'function');
assert(rule && typeof rule === 'object' && typeof rule.create === 'function');

Conditions that only happen to involve the same value are still independent and are reported. In particular, comparing a property is a check on that property rather than on the object that holds it:

import assert from 'node:assert/strict';

assert(index >= 0 && index < items.length); // Noncompliant: two independent bounds
assert(result.kind === 'success' && result.value); // Noncompliant: two independent properties

What is the potential impact?

When a composite assertion fails, developers spend extra time deciphering which condition broke. Opaque failure messages slow down debugging and make flaky or partial regressions harder to diagnose. Splitting assertions keeps each check independent and produces clearer failure reports.

How to fix it

Split composite assertions into separate assertion statements, one per independent condition. For assert.ok(!(a || b)) or expect(a || b).toBeFalsy(), write separate assertions for each condition.

This applies to:

Plain a || b truthiness assertions are not reported because replacing them with separate assertions would change the test to require both conditions. Comparisons such as expect(a && b).toBe(true) are also outside the scope of this rule.

Code examples

Noncompliant code example

import assert from 'node:assert/strict';

assert.ok(user.isActive && user.isVerified); // Noncompliant: This composite assertion hides which condition failed; split it to make failures actionable

Compliant solution

import assert from 'node:assert/strict';

assert.ok(user.isActive);
assert.ok(user.isVerified);

Noncompliant code example

import assert from 'node:assert/strict';

assert(!(user.isBlocked || user.isSuspended)); // Noncompliant: This composite assertion hides which condition failed; split it to make failures actionable

Compliant solution

import assert from 'node:assert/strict';

assert(!user.isBlocked);
assert(!user.isSuspended);

Noncompliant code example

import { expect } from 'vitest';

expect(user.isActive && user.isVerified).toBeTruthy(); // Noncompliant: This composite assertion hides which condition failed; split it to make failures actionable

Compliant solution

import { expect } from 'vitest';

expect(user.isActive).toBeTruthy();
expect(user.isVerified).toBeTruthy();

Noncompliant code example

import { expect } from 'bun:test';

expect(user.isBlocked || user.isSuspended).toBeFalsy(); // Noncompliant: This composite assertion hides which condition failed; split it to make failures actionable

Compliant solution

import { expect } from 'bun:test';

expect(user.isBlocked).toBeFalsy();
expect(user.isSuspended).toBeFalsy();

Noncompliant code example

import { expect } from '@jest/globals';

expect(user.isBlocked || user.isSuspended).not.toBeTruthy(); // Noncompliant: This composite assertion hides which condition failed; split it to make failures actionable

Compliant solution

import { expect } from '@jest/globals';

expect(user.isBlocked).not.toBeTruthy();
expect(user.isSuspended).not.toBeTruthy();

Noncompliant code example

import { expect } from 'bun:test';

expect(!(user.isActive && user.isVerified)).toBeFalsy(); // Noncompliant: This composite assertion hides which condition failed; split it to make failures actionable

Compliant solution

import { expect } from 'bun:test';

expect(user.isActive).toBeTruthy();
expect(user.isVerified).toBeTruthy();

Resources

Documentation