Search code examples
angulartestingjasminekarma-jasmine

karma angular test router navigate


I'm developing an angular exercising app and testing it with Karma and Jasmine. I'm trying to test router's navigate functionality. The problem is that this call is inside a then(). I suppose something is not working because is async. The error: Expected spy navigate to have been called with:[ [ '/error' ] ] but it was never called. How can I resolve it? My ts:

  myFunction(): void {
    functionWhichReturnsPromise().then(() => {
      this.router.navigate('/error');
    });
  }

Spec file:

describe('myComponent', () => {
  let component: myComponent;
  let fixture: ComponentFixture<myComponent>;
  let router: Router;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      declarations: [myComponent],
      providers: [],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    }).compileComponents();

    fixture = TestBed.createComponent(myComponent);
    router = TestBed.inject(Router);
    component = fixture.componentInstance;
    spyOn('functionWhichReturnsPromise').and.returnValue(Promise.resolve());
    fixture.detectChanges();
  });

  it('should call navigate', () => {
    const spyRouter = spyOn(router, 'navigate').and.resolveTo(true);

    component.myFunction();

    expect(spyRouter).toHaveBeenCalledWith(['/error']);
  });
});

Solution

  • Angular lets you synchronously simulate the passing of time of an async function by wrapping your test function in fakeAsync, then using tick(ms) where ms is the number of millisecond you want ellapsed.

    With your code:

     it('should call navigate', fakeAsync(() => {
        const spyRouter = spyOn(router, 'navigate').and.resolveTo(true);
    
        component.myFunction();
        tick() //might need to specify a number of millisecond 
               //depending on what 'myFunction' does
    
        expect(spyRouter).toHaveBeenCalledWith(['/error']);
      }));
    

    FakeAsync doc
    Note that in you case, depending on what your promise does, flushMicrotasks() might also work instead of tick()