2

I have a strange behavior on testing Angular RxJS based funtions with Karma/Jasmine.

I have this test:

describe('LoginComponent', () => {
  let fixture: ComponentFixture<LoginComponent>;
  let component: LoginComponent;
  let debugElement: DebugElement;
  let location: Location;
  let router: Router;
  let authServiceSpy: AuthService = {
    login: () => null,
    user: new BehaviorSubject<UserInterface>(null)
  } as jasmine.SpyObj<any>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule,
        RouterTestingModule,
        AppModule,
      ],
      providers: [
        { provide: AuthService, useValue: authServiceSpy },
      ],
      declarations: [
        LoginComponent,
      ]
    })
    .compileComponents()
    .then(() => {
      router = TestBed.inject(Router);
      location = TestBed.inject(Location);
      fixture = TestBed.createComponent(LoginComponent);
      component = fixture.componentInstance;
      debugElement = fixture.debugElement;
    });
  });

  describe('submit form', () => {
    it('should navigate to dashboard with correct credentials', fakeAsync(() => {
      spyOn(authServiceSpy, 'login').and.returnValue(of(new User()));
      const navSpy = spyOn(router, 'navigateByUrl');
      const button = debugElement.queryAll(By.css('.login-button'));
      fixture.detectChanges();
      fixture.whenStable().then(() => {
        button[0].nativeElement.click();
        tick();
        expect(navSpy).toHaveBeenCalledTimes(1);
        expect(navSpy).toHaveBeenCalledWith('/dashboard');
      });
    }));

    it('should show error with incorrect credentials', fakeAsync(() => {
      spyOn(authServiceSpy, 'login').and.returnValue(throwError('login failed'));
      const navSpy = spyOn(router, 'navigateByUrl');
      const button = debugElement.queryAll(By.css('.login-button'));

      fixture.detectChanges();
      fixture.whenStable().then(() => {
        button[0].nativeElement.click();
        tick();
        fixture.detectChanges();

        expect(navSpy).toHaveBeenCalledTimes(0);
      });
    }));
  });
});

After run these tests I get this error message:

Error: 1 timer(s) still in the queue.

Then I add flush() for the start of both it() funtions. And I get this error:

Expected spy navigateByUrl to have been called 0 times. It was called 1 times.

I tried to use discardPeriodicTasks() and flushMicrotasks() too. But not helped.

When I use it() functions one-by-one with fit() then all works correctly.

Any idea how can I fix this?

netdjw
  • 5,419
  • 21
  • 88
  • 162
  • Hello @netdjw, what happens if you run only `'should show error with incorrect credentials'`? – rmjoia Mar 16 '22 at 13:13
  • Hi @rmjoia, that works fine. The other one is works fine if I run it alone. If both is running I get errors. – netdjw Mar 16 '22 at 13:41
  • @netdhw that's what I thought. I think that the problem is that it's keeping the state since it's not reset between tests, the 1 you get is from the other test. – rmjoia Mar 16 '22 at 13:45
  • @rmjoia Yes, I'm on same opinion, but how can I fix this issue? – netdjw Mar 16 '22 at 13:46
  • what happens if you remove the `describe('submit form')...`? does it work? Just curious about something. You're nesting those tests in a new describe block, pull them outside "to the main describe" or just remove it and run – rmjoia Mar 16 '22 at 13:56
  • I tried this sceanrio too, but nothing new. – netdjw Mar 16 '22 at 13:58
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/242987/discussion-between-rmjoia-and-netdjw). – rmjoia Mar 16 '22 at 14:01

1 Answers1

3

As pointed out by @rmjoia, you need to reset the spy object as it will be reused on related test specs. Resetting can be accomplished through the reset method.

navSpy.calls.reset

I would recommend doing this on the beforeEach function.

Another solution would be splitting the tests into two test suites

netdjw
  • 5,419
  • 21
  • 88
  • 162
Murage
  • 509
  • 3
  • 4
  • It sounds good, but when I add this code to my `beforeEach`: `authServiceSpy.calls.reset();` It says: `Property 'calls' does not exist on type 'AuthService'`. Same result if I add it after a `spyOn()` call. – netdjw Mar 23 '22 at 16:31
  • I fixed the answer to `navSpy.calls.reset`. This is works, but only after `fixture.detectChanges();` – netdjw Mar 24 '22 at 10:35