I have the following component and service:
@Component({
selector: 'app-root',
template: '',
})
export class AppComponent implements OnInit {
constructor(private service: Service) {}
ngOnInit(): void {
console.log('Init with', this.service.val);
}
getValue() {
return this.service.val;
}
}
@Injectable({
providedIn: 'root',
})
export class Service {
val: number = 0;
}
In my tests, I want to change the value of val from the service, for a particular test, and then run the component's onInit again:
describe('AppComponent', () => {
let comp: AppComponent;
let service: jasmine.SpyObj<Service>;
beforeEach(() => {
service = jasmine.createSpyObj('service', [], { val: 1 });
TestBed.configureTestingModule({
declarations: [AppComponent],
providers: [{ provide: Service, useValue: service }],
});
const fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
fixture.detectChanges();
});
it('service value is 1', () => {
expect(comp.getValue()).toBe(1);
});
it('override service value to 2', () => {
service.val = 2;
TestBed.inject(Service).val = 2;
expect(comp.getValue()).toBe(2); // failed: Expected 1 to be 2.
comp.ngOnInit();
});
});
In the second test, I would expect getValue()
from the component to return the updated service value (2), yet 1 is returned. Also ngOnInit
prints "Init with 1" three(as expected) times, I would expect the last print to be "Init with 2". What's going on?
You have to do the following to change the value of instance variables on a spy object:
it('override service value to 2', () => {
(Object.getOwnPropertyDescriptor(service, 'val')?.get as jasmine.Spy<() => number>).and.returnValue(2);
expect(comp.getValue()).toBe(2); // Should work now
comp.ngOnInit();
});
Full answer here: https://stackoverflow.com/a/64560773/7365461