Rules that enforce best practices for Jasmine spy usage and management to ensure reliable test isolation and clear spy behavior.
Encourages the use of named spies to improve test readability and debugging capabilities.
/**
* Rule: named-spy
* Encourages naming spies for better debugging
* @config [0|1|2] - Rule severity level
*/
'jasmine/named-spy': [0|1|2]Examples:
// ❌ Potentially unclear - anonymous spy
spyOn(service, 'getData');
// ✅ Better - named spy for clarity
spyOn(service, 'getData').and.callFake(function mockGetData(id) {
return { id: id, name: 'Test User' };
});
// ✅ Good - using jasmine.createSpy with name
const mockCallback = jasmine.createSpy('callback spy');Prevents assignment of spyOn return values to variables, which can lead to confusion about spy behavior and lifecycle.
/**
* Rule: no-assign-spyon
* Disallows assigning spyOn results to variables
* @config [0|1|2] - Rule severity level
*/
'jasmine/no-assign-spyon': [0|1|2]Examples:
// ❌ Bad - assigning spyOn result
const spy = spyOn(service, 'getData');
spy.and.returnValue('mocked data');
// ✅ Good - chain spy configuration
spyOn(service, 'getData').and.returnValue('mocked data');
// ✅ Good - use createSpy for reusable spies
const reusableSpy = jasmine.createSpy('getData spy');
service.getData = reusableSpy;Prevents potentially unsafe spy patterns that could lead to unreliable tests or unexpected behavior.
/**
* Rule: no-unsafe-spy
* Disallows unsafe spy usage patterns
* @config [0|1|2] - Rule severity level
*/
'jasmine/no-unsafe-spy': [0|1|2]Common unsafe patterns:
Examples:
// ❌ Bad - spying on non-existent method
spyOn(service, 'nonExistentMethod'); // Runtime error
// ❌ Bad - spying on property that's not a function
spyOn(service, 'config'); // config is an object, not a function
// ✅ Good - spy on existing methods
spyOn(service, 'getData').and.returnValue('test data');
// ✅ Good - check method exists before spying
if (typeof service.optionalMethod === 'function') {
spyOn(service, 'optionalMethod');
}All spy management 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/named-spy: 0 # Optional - style preference
jasmine/no-assign-spyon: 0 # Optional - some patterns are valid
jasmine/no-unsafe-spy: 1 # Warning - helps catch errorsdescribe('Service with dependencies', function() {
let service, mockDependency;
beforeEach(function() {
// Create mock dependency
mockDependency = jasmine.createSpyObj('dependency', [
'fetchData',
'saveData',
'validateData'
]);
service = new Service(mockDependency);
});
it('should call dependency methods', function() {
// Configure spy behavior
mockDependency.fetchData.and.returnValue('test data');
mockDependency.validateData.and.returnValue(true);
// Execute code under test
const result = service.processData(123);
// Verify spy interactions
expect(mockDependency.fetchData).toHaveBeenCalledWith(123);
expect(mockDependency.validateData).toHaveBeenCalledWith('test data');
expect(result).toBe('processed: test data');
});
});describe('Component with external dependencies', function() {
let originalMethod;
beforeEach(function() {
// Store original method for restoration
originalMethod = ExternalService.prototype.apiCall;
// Create spy
spyOn(ExternalService.prototype, 'apiCall')
.and.returnValue(Promise.resolve('mocked response'));
});
afterEach(function() {
// Jasmine automatically restores spies, but explicit cleanup is good practice
ExternalService.prototype.apiCall = originalMethod;
});
it('should use mocked external service', async function() {
const component = new Component();
const result = await component.performAction();
expect(ExternalService.prototype.apiCall).toHaveBeenCalled();
expect(result).toContain('mocked response');
});
});describe('Complex spy scenarios', function() {
it('should handle different call scenarios', function() {
const mockService = jasmine.createSpyObj('service', ['process']);
// Different return values for different calls
mockService.process.and.returnValues(
'first call result',
'second call result',
'default result'
);
expect(mockService.process()).toBe('first call result');
expect(mockService.process()).toBe('second call result');
expect(mockService.process()).toBe('default result');
});
it('should use custom fake implementation', function() {
const calculator = { add: function(a, b) { return a + b; } };
spyOn(calculator, 'add').and.callFake(function(a, b) {
console.log(`Adding ${a} + ${b}`);
return a + b + 1; // Modified behavior for testing
});
const result = calculator.add(2, 3);
expect(result).toBe(6); // 2 + 3 + 1
expect(calculator.add).toHaveBeenCalledWith(2, 3);
});
});