In an Angular environment, how can I very easily create in a Jest environment a mocked service for a service object returning a specific value? This could be via Jest of ng-mocks, etc.
An oversimplified example:
// beforeEach:
// setup an Angular component with a service component myMockService
// Test 1:
// fake "myMockService.doSomething" to return value 10
// expect(myComponent.getBalance()).toEqual( "Your balance: 10");
// Test 2:
// fake "myMockService.doSomething" to return value 20
// expect(myComponent.getBalance()).toEqual( "Your balance: 20");
I have studied the Jest and ng-mocks docs but didn't find a very easy approach. Below you find 2 working approaches. Can you improve the version?
My simplified Angular component:
@Component({
selector: 'app-servicecounter',
templateUrl: './servicecounter.component.html'
})
export class ServicecounterComponent {
private myValue: number = 1;
constructor(private counterService: CounterService) { }
public doSomething(): void {
// ...
myValue = this.counterService.getCount();
}
}
This is the simplified service:
@Injectable()
export class CounterService {
private count = 0;
constructor() { }
public getCount(): Observable<number> {
return this.count;
}
public increment(): void {
this.count++;
}
public decrement(): void {
this.count--;
}
public reset(newCount: number): void {
this.count = newCount;
}
}
Try 1: a working solution: with 'jest.genMockFromModule'.
The disadvantage is that I can only create a returnValue only at the start of each series of tests, so at beforeEach setup time.
beforeEach(async () => {
mockCounterService = jest.genMockFromModule( './counterservice.service');
mockCounterService.getCount = jest.fn( () => 3);
mockCounterService.reset = jest.fn(); // it was called, I had to mock.fn it.
await TestBed.configureTestingModule({
declarations: [ServicecounterComponent],
providers: [ { provide: CounterService, useValue: mockCounterService }],
}).compileComponents();
fixture = TestBed.createComponent(ServicecounterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('shows the count', () => {
setFieldValue(fixture, 'reset-input', String(currentCount));
click(fixture, 'reset-button');
expect(mockCounterService.getCount()).toEqual( 3);
expect( mockCounterService.getCount).toBeCalled();
});
Try 2: replace 'jest.genMockFromModule' with 'jest.createMockFromModule': works equally well.
The disadvantage is still that I can create a returnValue only at the start of each series of tests, so at beforeEach setup time.
Try 3: create a mock object upfront: didn't work
jest.mock( "./counterservice.service");
beforeEach(async () => {
// Create fake
mockCounterService = new CounterService();
(mockCounterService.getCount() as jest.Mock).mockReturnValue( 0);
await TestBed.configureTestingModule({
declarations: [ServicecounterComponent],
providers: [{ provide: CounterService, useValue: mockCounterService }],
}).compileComponents();
fixture = TestBed.createComponent(ServicecounterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('shows the count', () => {
// do something that will trigger the mockCountService getCount method.
expect(mockCounterService.getCount).toEqual( 0);
});
This doesn't work, giving the error:
> (mockCounterService.getCount() as jest.Mock).mockReturnValue( 0);
> Cannot read property 'mockReturnValue' of undefined
Try 4: with .fn(). The disadvantage is that the original class may change, then the test object MUST change.
beforeEach(async () => {
mockCounterService = {
getCount: jest.fn().mockReturnValue( 0),
increment: jest.fn,
decrement: jest.fn(),
reset: jest.fn
};
await TestBed.configureTestingModule({
declarations: [ServicecounterComponent],
providers: [{ provide: CounterService, useValue: mockCounterService }],
}).compileComponents();
});
it( '... ', () => {
// ...
expect(mockCounterService.reset).toHaveBeenCalled();
});
This time, the error is:
> Matcher error: received value must be a mock or spy function ...
> expect(mockCounterService.reset).toHaveBeenCalled();
Can you help improving this way of working?