0

In my Angular Unit Test I want to check whether myComponent.items$ have 0 results when I call subject myComponent.clearInput$.next(). Before that I want to ensure that there are some records. myComponent.items$ can be populated by myComponent.nameInput$.next("test").

Unfortunately both subscription are triggered after both Subjects have been called, so myComponent.items$ is always 0.

it("when control touched then clear input value", done => {

    myComponent.items$.pipe(first()).subscribe(result => {
        expect(result.length).toBe(2); 
        // it's always 0 because of myComponent.clearInput$.next(); from last line
    })
    myComponent.nameInput$.next("test");

// Now should wait after code from above will finish then the rest should be executed after that.

    myComponent.items$.pipe(first()).subscribe(result => {
        expect(result.length).toBe(0);
        done(); // test should be finished here
    })
    myComponent.clearInput$.next();
});

This is how is items$ is called by those Subjects

this.items$ = merge(
    this.nameInput$.pipe(
        switchMap((name: string) =>
            iif(() => this._partyType === "PT",
                this.someService.getByName(name),
                this.otherService.getByOtherName(name)
            )
        )
    ),
    this.clearInput$.pipe(
        switchMapTo(of([]))
    )
);
DiPix
  • 5,755
  • 15
  • 61
  • 108

1 Answers1

0

In my unit tests, I rarely subscribe. I use promise approach because then I can wait.

Try this:

it("when control touched then clear input value", async done => {
   myComponent.nameInput$.next("test");
   console.log('first await...');
   const result = await myComponent.items$.pipe(take(1)).toPromise();
   console.log('after first await...');
   expect(result.length).toBe(2);

   myComponent.clearInput$.next();
   console.log('second await...');
   const newResult = await myComponent.items$.pipe(take(1)).toPromise();
   console.log('after second await...');
   expect(newResult.length).toBe(0);
   done();
});

I think that's what you want. This way we can have blocking code and much better assertions.

AliF50
  • 16,947
  • 1
  • 21
  • 37
  • I have an error: Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL) – DiPix Mar 13 '20 at 17:17
  • Okay, the test is getting stuck on a `Promise`. Make sure the test is structured well and the component as well. I added `console.logs`, see which `after ...` log you don't see and that's the promise that has never been resolved. – AliF50 Mar 13 '20 at 17:22
  • `console.log('after first await...');` is never triggered – DiPix Mar 13 '20 at 17:46
  • That means `items$` does not trigger. How are `nameInput$` and `items$` related? – AliF50 Mar 13 '20 at 17:48
  • I think it's because `nameInput$` is a Subject (not ReplaySubject) so doesn't have any cached value when we call `toPromise()` – DiPix Mar 13 '20 at 17:51
  • Can you show your component code and how `nameInput$` and `items$` are related? Where basically when `nameInput$` emits, `items$` emits as well? – AliF50 Mar 13 '20 at 17:53
  • Updated answer. When I changed it to `ReplaySubject` it worked but this is isn't good solution for me. – DiPix Mar 13 '20 at 18:00
  • What is `iif(() => this._partyType === "PT", this.someService.getByName(name), this.otherService.getByOtherName(name) )`, I bet this is causing the issue. When it goes into the `switchMap`, does the `switchMap` return an observable? – AliF50 Mar 13 '20 at 18:01
  • Even if I will remove this and replace with just `this.otherService.getByOtherName(name)` in switchMap - still doesn't work. Service working fine (I checked with console.log inside) – DiPix Mar 13 '20 at 18:10