I have got several of the following test cases which run successfully when executed individually but they randomly fail when run in group. They all use setTimeout
. They are in single spec
file segregated in separate describe
methods.
Eg. This test case (which uses setTimeout
) passes when I run it by itself but when I run it in a group, it fails. I suspect the issue has to do something with setTimeout
. I tried using done
but that isn't solving the issue.
describe('AppComponent Test suite', () => {
let component: AppComponent;
let fixture: ComponentFixture<AppComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,
...
],
imports: [
....
],
providers: [{provide: APP_BASE_HREF, useValue: '/'},
....]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
let componentDE = fixture.debugElement;
let componentNE:HTMLElement = componentDE.nativeElement;
componentNE.setAttribute("signup","someIncorrectValue");
fixture.detectChanges();
});
it('should show dialog message if the application has unrecognised value of signup attribute in url',(done)=>{
spyOn(component,'showDialog');
setTimeout(()=>{
expect(component.showDialog).toHaveBeenCalledWith("Unrecognised message: someIncorrectValue",jasmine.any);
},1000); done();
});
});
Imagine more similar test cases inthe same file each in its own describe
, all using setTimeout
.
Why are they failing? If the issue is synchronzation, how do I synchronize them?
UPDATE
I tried async
/await
as well but no joy!
it('should show dialog message if the application has unrecognised value of signup attribute in url',async ()=>{
spyOn(component,'showDialog');
await setTimeout(()=>{
expect(component.showDialog).toHaveBeenCalledWith("Unrecognised message: someIncorrectValue",jasmine.any);
},1000);
});
I also moved the call to done
inside setTimeout
's callback but no joy there as well.
it('should show dialog message if the application has unrecognised value of signup attribute in url', (done)=>{
spyOn(component,'showDialog');
setTimeout(()=>{
expect(component.showDialog).toHaveBeenCalledWith("Unrecognised message: someIncorrectValue",jasmine.any);
done();
},1000);
});
The component is the entry point of the application and is invoked by passing a signup
attribute in the url
.
<app-root signup=@signup>
The signup tells angular at startup whether the app got started because user clicked a signup link. Accordingly, angular can show a message whether the signup was successful or not. Earlier I faced the following issue (Getting ExpressionChangedAfterItHasBeenCheckedError error I update property of a Component from another Component) and to resolve it, I added a setTimeout
in the ngAfterViewInit
of the app component
ngAfterViewInit(){
setTimeout(()=>{
this.checkIfSignupProcess();
this.authGuard.authGuardError$.subscribe((authGuardContext:AuthGuardContext)=>{
this.handleAuthGuardContextMessage(authGuardContext);
});
this.dialogService.dialogMessage$.subscribe((message:DialogServiceMessageContext)=>{
console.log("received message from Dialog box service");
this.handleDialogServiceMessage(message);
})
},100);
/*
OR
this.isSignupProcess(); //this will cause ExpressionChangedAfterItHasBeenCheckedError error s the function changes message of DialogComponent but the change detection isn't complete.
this.cd.detectChanges();
*/
}
UPDATE 2
I tried using 'tick' but the test still fails, even when run individually.
fit('should show dialog message if the application has unrecognised value of signup attribute in url', ()=>{
jasmine.clock().install();
spyOn(component,'showDialog');
setTimeout(() => {
console.log("in spec's timeout");
expect(component.showDialog).toHaveBeenCalledWith("Unrecognised message: someIncorrectValue",jasmine.any);
},1000);
jasmine.clock().tick(1001);
jasmine.clock().uninstall();
/*setTimeout(()=>{
expect(component.showDialog).toHaveBeenCalledWith("Unrecognised message: someIncorrectValue",jasmine.any);
done();
},1000);*/
});
The reason for failure is Expected spy showDialog to have been called with [ 'Unrecognised message: someIncorrectValue', Function ] but it was never called.
UPDATE 3
I notice that even though the the timeout value is 100 in my component and 1000 in my specs, the specs timeout expires first!!!
This code from my component which should show the dialog box is called after my spec's code even though the timeout value in component is 100 compared to 10000 in the specs!!
component's timeout
setTimeout(()=>{
console.log("timeout of component");
this.checkIfSignupProcess();
...,100
}
spec's timeout
fit('should show dialog message if the application has unrecognised value of signup attribute in url', ()=>{
jasmine.clock().install();
spyOn(component,'showDialog');
setTimeout(() => {
console.log("in spec's timeout 10000");
expect(component.showDialog).toHaveBeenCalledWith("Unrecognised message: someIncorrectValue",jasmine.any);
},10000);
jasmine.clock().tick(10001);
jasmine.clock().uninstall();
/*setTimeout(()=>{
expect(component.showDialog).toHaveBeenCalledWith("Unrecognised message: someIncorrectValue",jasmine.any);
done();
},1000);*/
});