noctcore-code-quality/no-vacuous-expect
A test must assert behaviour that a real regression would break.
Recommended preset: error · Autofix: no · Suggestions: no · Type information: not needed
Some assertions pass for almost any implementation. expect(typeof handler).toBe('function') proves
a binding exists. expect(true).toBe(true) cannot fail. A test whose only assertion is
toBeDefined() or toBeTruthy() stays green when the function returns the wrong object, the wrong
string or the wrong number. These tests add to the count and protect nothing.
The rule matches on method names, not on a runner import, so it covers Jest, Vitest and Bun alike.
What it flags
Section titled “What it flags”typeofExpect:expect(typeof x).toBe('<typeof result>'), also withtoEqual,toStrictEqualand.not.tautologyExpect:expect(<literal>).toBe(<same literal>), also withtoEqual/toStrictEqual.soleWeakExpect: a test (it/test, including.concurrent,.eachand other modifiers) whose only assertion is a weak matcher.
it('should be defined', () => { expect(service).toBeDefined();});
it('returns a token', () => { expect(typeof issueToken()).toBe('string');});
it('works', () => { expect(true).toBe(true);});it('issues a signed token for the user', () => { const token = issueToken({ userId: 'u-1' }); expect(verify(token)).toEqual({ userId: 'u-1' });});
it('creates the user', () => { const user = create({ name: 'ada' }); expect(user).toBeDefined(); expect(user.name).toBe('ada');});
it('clears the key', () => { cache.delete('k'); expect(cache.get('k')).toBeUndefined(); // a specific absence, not a weak check});What it does not flag
Section titled “What it does not flag”- A weak matcher next to any other assertion. A test counts every
expect(...)matcher plus any call matchingassertionCallees, so a weakexpectnext toassert.equal(...),expectValidUser(...)or supertest’s.expect(200)is fine. toBeUndefined,toBeNullandnot.toBeNull, unless you add them toweakMatchers(see Options).
Limitations
Section titled “Limitations”Assertions are counted syntactically inside the test callback. An assertion hidden in a helper whose
name does not match assertionCallees is not seen. A sole toBeTruthy() on a Testing Library
getBy* query is reported even though the query itself throws when the element is missing; assert
with a matcher that states the intent instead.
Options
Section titled “Options”| Option | Type | Default | Meaning |
|---|---|---|---|
weakMatchers |
string[] |
["toBeDefined", "toBeTruthy", "toBeFalsy", "not.toBeUndefined"] |
Matchers that cannot carry a test alone. Prefix not. for the negated form. |
assertionCallees |
string[] (regex sources) |
["^assert", "^expect\\w", "\\.expect$"] |
Calls that also count as an assertion. Matched against name, obj.name (member on an identifier) or .name (any other member). |
toBeUndefined, toBeNull and not.toBeNull are not weak by default: each pins one specific
value, and expect(container.querySelector('nav')).not.toBeNull() is a real presence check.
// Treat toBeUndefined as weak too, and count a project helper as an assertion.'noctcore-code-quality/no-vacuous-expect': ['error', { weakMatchers: ['toBeDefined', 'toBeTruthy', 'toBeFalsy', 'toBeUndefined'], assertionCallees: ['^assert', '^expect\\w', '\\.expect$', '^verifySnapshot$'],}]When not to use it
Section titled “When not to use it”In a smoke suite whose only purpose is to prove modules load.
Credits
Section titled “Credits”Based on a rule from tsforge (MIT). See THIRD_PARTY_NOTICES.md.