1

I'm writing a test that each INPUT field should be not-empty. Either I don't know how to write this test (likely) or Jasmine can't handle multiple actions in a single it().

Here is something like what I tried to write:

it('fails to login for blank credential fields', async(() => {
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      comp = fixture.componentInstance;
      de = fixture.debugElement;
      let elLoginButton = de.query(By.css('#login'));

      comp.name = '';
      comp.password = 'filled';
      elLoginButton.triggerEventHandler('click', null);

      fixture.detectChanges();
      fixture.whenStable().then(() => {
        expect(comp.name).toEqual('', 'name of (blank, filled) test');
        expect(comp.password).toEqual('filled', 'password of (blank, filled) test');
      });

      comp.name = 'filled';
      comp.password = '';
      elLoginButton.triggerEventHandler('click', null);

      fixture.detectChanges();
      fixture.whenStable().then(() => {
        expect(comp.name).toEqual('filled', 'name of (filled, blank) test');
        expect(comp.password).toEqual('', 'password of (filled, blank) test');
      });

    });    

}));

I intend that the (filled, blank) test be executed and its expect() actions evaluated, then the (blank, filled) test be completed.

What I'm seeing in the debugger is that the background process for "click" is done for the (filled, blank) test is run. Then the background process for (blank, filled) is run. Then all of the expects() are run. This means that the (filled, blank) versions of expect() see a "comp" from the (blank, filled) test.

In the end, I created two different it() tests. Is there a better way than "two separate tests" for this?

Makoto
  • 104,088
  • 27
  • 192
  • 230
Jerome P Mrozak
  • 1,907
  • 4
  • 21
  • 33

1 Answers1

2

It's because you're trying to mix asynchronous programming with synchronous programming. When you call fixture.whenStable(), you're waiting for an asynchronous task to resolve. But before it's resolved, you are trying to make more synchronous calls

fixture.whenStable().then(() => {
  expect(comp.name).toEqual('', 'name of (blank, filled) test');
  expect(comp.password).toEqual('filled', 'password of (blank, filled) test');
});

comp.name = 'filled';
comp.password = '';
elLoginButton.triggerEventHandler('click', null);

So ultimately, if you want to make more changes and assertions, you need to wait for the previous asynchronous task to resolve

fixture.whenStable().then(() => {
  expect(comp.name).toEqual('', 'name of (blank, filled) test');
  expect(comp.password).toEqual('filled', 'password of (blank, filled) test');

  comp.name = 'filled';
  comp.password = '';
  elLoginButton.triggerEventHandler('click', null);

  fixture.detecChanged();
  fixture.whenStable().then(() => {
    expect(..)

    // do more changes and assertions
  })
});

The more changes and assertions you want to make (that are asynchronous), the deeper in nesting you need to go.

If you want to avoid this ugly beast, then consider using fakeAsync/tick instead. Calling tick is essentially like using whenStable() except that you can program in a synchronous way. The tick() call will wait for asynchronous tasks to complete, making it appear to be synchronous.

import { fakeAsync, tick } from '@angular/core/testing';

it('', fakeAsync(() => {
  fixture.detectChanges();
  tick()
  expect(..)

  changeSomething()
  fixture.detectChanges()
  tick()
  expect()
}))

See also:

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720