Safiyya
Safiyya

Reputation: 1393

How to test a RxJs callback in jasmine

I'm using NgbTypeAhead in my component where the callback must be a (text: Observable<string>) => Observable<any[]> . Now, I want to test the side effects of the search but I'm struggling to write that test in jasmine.

Can anybody help ? Thanks in advance.

component.ts

search = (text$: Observable<string>) =>
        text$
            .debounceTime(200)
            .distinctUntilChanged()
            .do((term: string) => {
                console.log("do 1", term)
                this.isSearching = true && term !== "";
                this.cd.markForCheck();
            })
            .map(term => {
                console.log("map", term)
                return term === ""
                    ? this.list
                    : this.filter(term)
            })
            .do(list => {
                console.log("do 2", list)
                this.searchResultsCount = list.length;
                this.cd.markForCheck();
            });

component.html

<input #initiativeSearch type="text" id="initiativeSearch" 
    [ngbTypeahead]="search"/>

component.spec.ts

 it("should update searching flags", () => {
       // how do I test this ? here is what I tried
        let spyObj = jasmine.createSpyObj<Observable<string>>("text$", ["debounceTime", "distinctUntilChanged", "do", "map"]);

        spyObj.debounceTime.and.returnValue(spyObj)
        spyObj.distinctUntilChanged.and.returnValue(spyObj)
        spyObj.do.and.returnValues(Observable.of("blabla"), Observable.of([]))
        spyObj.map.and.returnValue(Observable.of([]))
        component.searchInitiatives(spyObj);

        expect(spyObj.debounceTime).toHaveBeenCalledWith(200);
        expect(spyObj.distinctUntilChanged).toHaveBeenCalledTimes(1);
        expect(spyObj.do).toHaveBeenCalledTimes(2);
    });

This logs spy text$.do to have been called 2 times. It was called 1 times. in the console and doesn't show any of the console.log

Upvotes: 2

Views: 1695

Answers (2)

iHazCode
iHazCode

Reputation: 771

You can test this functionality but you will want to test it outside of Angular's "home zone" with the fakeAsync decorator.

That said I HIGHLY recommend against this. To me it seems you are trying to test ReactiveX operators which is not in scope for your unit test. When using these libraries it should be assumed that the library has it's own unit tests. You may want to test that the correct value in milliseconds is being passed in, but you don't actually need or want to test the debounceTime operator.

Please let us know if this makes sense, if not we can find other better ways to find coverage for your assertion, but testing the RXJS framework is really not something I'd recommend focusing your energy on.

Upvotes: 0

Andrew Eisenberg
Andrew Eisenberg

Reputation: 28737

It sounds like you are testing in the wrong direction. If you are unit testing, all you need to do is test a single component at a time.

You just need to test your search component directly. You can do something like this:

let observableSpy = {
  debounceTime: observableSpy,
  distinctUntilChanged: observableSpy,
  // ... repeat for all properties
};
let spyObj = jasmine.createSpyObj(observableSpy);

Then pass spyObj to your search function:

search(spyObj)

Finally, run some assertions:

expect(spyObj.debounceTime).toHaveBeenCalledWith(...);
// ... test all calls.

Doing it this way would be more of a unit test.

Upvotes: 1

Related Questions