Rules that encourage usage of Jasmine-specific matchers over generic alternatives to improve test expressiveness and error messaging.
Encourages the use of Jasmine's built-in matchers instead of generic equality checks, providing better error messages and more expressive test code.
/**
* Rule: prefer-jasmine-matcher
* Prefer Jasmine-specific matchers over generic alternatives
* @config [0|1|2] - Rule severity level
*/
'jasmine/prefer-jasmine-matcher': [0|1|2]Examples:
// ❌ Discouraged - generic equality checks
expect(value === null).toBe(true);
expect(array.length === 0).toBe(true);
expect(typeof result === 'undefined').toBe(true);
expect(fn.toString().indexOf('async') !== -1).toBe(true);
// ✅ Better - Jasmine-specific matchers
expect(value).toBeNull();
expect(array).toEqual([]);
expect(result).toBeUndefined();
expect(fn).toContain('async');
// ❌ Discouraged - complex boolean checks
expect(user.isActive && user.hasPermission).toBe(true);
expect(!error.isEmpty()).toBe(true);
// ✅ Better - specific matchers with clear intent
expect(user.isActive).toBe(true);
expect(user.hasPermission).toBe(true);
expect(error.isEmpty()).toBe(false);Encourages the use of toHaveBeenCalledWith for spy assertions instead of generic spy call checking.
/**
* Rule: prefer-toHaveBeenCalledWith
* Prefer toHaveBeenCalledWith over generic spy call checks
* @config [0|1|2] - Rule severity level
*/
'jasmine/prefer-toHaveBeenCalledWith': [0|1|2]Examples:
// ❌ Discouraged - generic call checking
expect(spy.calls.count()).toBe(1);
expect(spy.calls.argsFor(0)).toEqual(['arg1', 'arg2']);
// ❌ Discouraged - manual argument verification
expect(spy).toHaveBeenCalled();
expect(spy.calls.mostRecent().args[0]).toBe('expectedArg');
// ✅ Better - specific matcher with arguments
expect(spy).toHaveBeenCalledWith('arg1', 'arg2');
expect(spy).toHaveBeenCalledTimes(1);
// ✅ Good - multiple call verification
expect(spy).toHaveBeenCalledWith('first call');
expect(spy).toHaveBeenCalledWith('second call');
expect(spy).toHaveBeenCalledTimes(2);Controls preference for toBeUndefined() matcher usage over other undefined checking patterns.
/**
* Rule: prefer-toBeUndefined
* Control preference for toBeUndefined matcher usage
* @config [0|1|2, mode] - Rule severity and preference mode
* @param mode 'always' | 'never' - When to prefer toBeUndefined
* @default mode 'always'
*/
'jasmine/prefer-toBeUndefined': [0|1|2, 'always' | 'never']Configuration Examples:
// Always prefer toBeUndefined
'jasmine/prefer-toBeUndefined': [1, 'always']
// Never prefer toBeUndefined (use generic checks)
'jasmine/prefer-toBeUndefined': [1, 'never']Examples:
// With 'always' mode:
// ❌ Discouraged
expect(value).toBe(undefined);
expect(typeof value).toBe('undefined');
// ✅ Preferred
expect(value).toBeUndefined();
// With 'never' mode:
// ❌ Discouraged
expect(value).toBeUndefined();
// ✅ Preferred
expect(value).toBe(undefined);All Jasmine-specific matcher rules support standard ESLint severity levels:
0 or "off" - Disable the rule1 or "warn" - Enable as warning2 or "error" - Enable as error (fails lint)Recommended Settings:
rules:
jasmine/prefer-jasmine-matcher: 1 # Warning - improves expressiveness
jasmine/prefer-toHaveBeenCalledWith: 1 # Warning - better spy testing
jasmine/prefer-toBeUndefined: 0 # Optional - style preferencedescribe('Error message examples', function() {
it('should demonstrate clear error messages', function() {
const user = { name: 'John', age: 25 };
// ❌ Generic check - unclear error message
// Error: Expected false to be true
expect(user.name === 'Jane').toBe(true);
// ✅ Specific matcher - clear error message
// Error: Expected 'John' to be 'Jane'
expect(user.name).toBe('Jane');
// ❌ Array length check - unclear
// Error: Expected false to be true
expect(user.hobbies.length > 0).toBe(true);
// ✅ Better - shows actual vs expected clearly
// Error: Expected [] not to be empty
expect(user.hobbies).not.toEqual([]);
});
});describe('Jasmine matcher showcase', function() {
let user, mockService;
beforeEach(function() {
user = {
name: 'Alice',
age: 30,
email: 'alice@example.com',
hobbies: ['reading', 'cycling'],
profile: {
isPublic: true,
lastLogin: new Date('2023-01-15')
}
};
mockService = jasmine.createSpyObj('service', ['save', 'validate', 'notify']);
});
describe('value matchers', function() {
it('should use appropriate matchers for different value types', function() {
// Null/undefined checks
expect(user.name).toBeDefined();
expect(user.middleName).toBeUndefined();
expect(user.deletedAt).toBeNull();
// Type checks
expect(user.age).toEqual(jasmine.any(Number));
expect(user.hobbies).toEqual(jasmine.any(Array));
expect(user.profile).toEqual(jasmine.any(Object));
// String matchers
expect(user.email).toContain('@');
expect(user.email).toMatch(/^[a-z]+@[a-z]+\.[a-z]+$/);
// Numeric comparisons
expect(user.age).toBeGreaterThan(18);
expect(user.age).toBeLessThanOrEqual(100);
expect(user.hobbies.length).toBeCloseTo(2, 0);
// Array/object matchers
expect(user.hobbies).toContain('reading');
expect(user.hobbies).toHaveSize(2);
expect(user.profile).toHaveProperty('isPublic');
expect(user.profile).toHaveProperty('isPublic', true);
});
});
describe('spy matchers', function() {
it('should use appropriate spy matchers', function() {
const userData = { name: 'Bob', email: 'bob@test.com' };
mockService.validate.and.returnValue(true);
mockService.save.and.returnValue({ id: 123 });
// Perform actions
const isValid = mockService.validate(userData);
if (isValid) {
const result = mockService.save(userData);
mockService.notify('user-saved', result);
}
// Spy assertions with specific matchers
expect(mockService.validate).toHaveBeenCalled();
expect(mockService.validate).toHaveBeenCalledWith(userData);
expect(mockService.validate).toHaveBeenCalledTimes(1);
expect(mockService.save).toHaveBeenCalledWith(userData);
expect(mockService.save).toHaveBeenCalledBefore(mockService.notify);
expect(mockService.notify).toHaveBeenCalledWith('user-saved', { id: 123 });
// Verify call order
expect(mockService.validate).toHaveBeenCalledBefore(mockService.save);
expect(mockService.save).toHaveBeenCalledBefore(mockService.notify);
});
it('should verify spy call patterns', function() {
mockService.save('data1');
mockService.save('data2');
mockService.save('data3');
// Verify multiple calls
expect(mockService.save).toHaveBeenCalledTimes(3);
expect(mockService.save).toHaveBeenCalledWith('data1');
expect(mockService.save).toHaveBeenCalledWith('data2');
expect(mockService.save).toHaveBeenCalledWith('data3');
// Verify call arguments
expect(mockService.save.calls.argsFor(0)).toEqual(['data1']);
expect(mockService.save.calls.argsFor(1)).toEqual(['data2']);
expect(mockService.save.calls.mostRecent().args).toEqual(['data3']);
});
});
describe('async matchers', function() {
it('should use async-specific matchers', async function() {
const successPromise = Promise.resolve('success');
const failurePromise = Promise.reject(new Error('failure'));
// Promise state matchers
await expectAsync(successPromise).toBeResolved();
await expectAsync(successPromise).toBeResolvedTo('success');
await expectAsync(failurePromise).toBeRejected();
await expectAsync(failurePromise).toBeRejectedWith(jasmine.any(Error));
await expectAsync(failurePromise).toBeRejectedWithError('failure');
});
});
});// Before: Generic boolean checks
expect(value === null).toBe(true);
expect(value !== null).toBe(true);
expect(value === undefined).toBe(true);
expect(typeof value === 'string').toBe(true);
expect(array.length === 0).toBe(true);
expect(array.length > 0).toBe(true);
// After: Jasmine-specific matchers
expect(value).toBeNull();
expect(value).not.toBeNull();
expect(value).toBeUndefined();
expect(value).toEqual(jasmine.any(String));
expect(array).toEqual([]);
expect(array).not.toEqual([]);
// Before: Manual spy verification
expect(spy.calls.count()).toBe(1);
expect(spy.calls.argsFor(0)[0]).toBe('arg1');
expect(spy.calls.argsFor(0)[1]).toBe('arg2');
// After: Specific spy matchers
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith('arg1', 'arg2');
// Before: Complex object/array checks
expect(obj.hasOwnProperty('prop')).toBe(true);
expect(array.indexOf('item') !== -1).toBe(true);
expect(str.indexOf('substring') >= 0).toBe(true);
// After: Expressive matchers
expect(obj).toHaveProperty('prop');
expect(array).toContain('item');
expect(str).toContain('substring');