Reputation: 11
I have a component that changes the value of a signal declared in a service on its initialization. I'm trying to test this declaration using Jest and spying on the set
method of the signal, but with no success.
Running Angular 18.2.8 and Jest 29.7.0.
Goal: To verify that the component sets the loading signal to true in its ngOnInit method.
service.ts
import { signal, Injectable } from '@angular/core';
@Injectable()
export class CardStateService {
loading = signal\<boolean\>(false);
}
component.ts
import { Component, OnInit, inject, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UntilDestroy } from '@ngneat/until-destroy';
import { CardStateService } from './card-state.service';
@UntilDestroy()
@Component({
selector: 'component-card',
templateUrl: './component.html',
styleUrls: \['./component.scss'\],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: \[ CommonModule\],
providers: \[CardStateService\],
})
export class Component implements OnInit {
stateService = inject(CardStateService);
ngOnInit(): void {
this.stateService.loading.set(true);
}
}
component.spec.ts
import { signal } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MockProvider } from 'ng-mocks';
import { CardStateService } from '../../shared/card-state.service';
import { Component } from './component';
describe('Component', () => {
let component: Component;
let fixture: ComponentFixture<Component>;
let cardStateServiceMock: CardStateService;
let mockStateService = {
loading: signal(false),
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [Component],
providers: [
MockProvider(CardStateService, mockStateService),
],
});
fixture = TestBed.createComponent(Component);
component = fixture.componentInstance;
cardStateServiceMock = TestBed.inject(CardStateService);
fixture.detectChanges();
});
it('should set loading to true when initializing component', () => {
const loadingSpy = jest.spyOn(mockStateService.loading, 'set')
component.ngOnInit();
expect(loadingSpy).toHaveBeenCalledWith(true);
});
});
Test result
Component› should set loading to true when initializing component
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: true
Number of calls: 0
Upvotes: 1
Views: 192
Reputation: 57986
We can achieve the passing testcase by using whenStable
which returns a promise.
We can, place our testcase inside the then block and validate it.
it('should set loading to true when initializing App', () => {
const loadingSpy = jest.spyOn(mockStateService.loading, 'set');
component.ngOnInit();
fixture.whenStable().then(() => {
expect(loadingSpy).toHaveBeenCalledWith(true);
});
});
As of now, the test case for testing will pass only when you place the expect block inside an asynchronous code block. So if you wrap the code inside a setTimeout
the code will pass, so asynchronous delay of the test case will make it pass, while synchronous execution is causing it to fail.
it('should set loading to true when initializing App', () => {
const loadingSpy = jest.spyOn(mockStateService.loading, 'set');
component.ngOnInit();
setTimeout(() => { // <- this also passes
expect(loadingSpy).toHaveBeenCalledWith(true);
});
});
But the techniques of flushEffects
/ async await
and fakeAsync flush
and detectChanges
are not working.
Upvotes: 0