Search code examples
angularunit-testingtestingjasminekarma-jasmine

How to trigger a service call and validate output length in a test with jasmine?


Problem (1) I am not able to find a way to trigger a service call in unit test which should be triggered when input is given.

(2)The length of output can be determined based on the input and the length has to be validated in unit test.

Code

    <form [formGroup]="sampleFormGroup">
        <ng-select class="default" [items]="listItems | async" bindLabel="id"
        formControlName="theList" [typeahead]="search" id="mylist">
        </ng-select>
    </form>

     export class AppComponent implements OnInit {
     search:Subject<string> = new Subject<string>();
     listItems: Observable<any>;
     testl:any;

     constructor(private formBuilder:FormBuilder,private dataService:ListService){}
     public sampleFormGroup = this.formBuilder.group({
       theList:[null]
     });

     ngOnInit(): void {
       this.listItems = this.search.pipe(
         debounceTime(500),
         switchMap(id => this.dataService.getList(id))
       );
     }
    }

    export class ListService {
     constructor(private http:HttpClient) {}

     getList(id:any) {
       return this.http.get('https://api.restful-api.dev/objects?id='+id);
     }
    }

    it(`should set the length of output observable array as 1`, () => {
        const fixture = TestBed.createComponent(AppComponent);
        const app = fixture.componentInstance;
        const list = [{"id":"3","name":"Apple"},{"id":"5","name":"Samsung"}];
        listService.getList.and.returnValue(of(list));
        component.search = new BehaviorSubject('3');
        component.listItems.subscribe(resp => {
          expect(resp.length).toBe(1);
        })
      });

Expected result There are 2 items in the mocked list. If the test input is 3 then the list in the output has 1 item which means that length of the output should be 1.


Solution

  • You have to write the function to filter the list on the service. Since you are working with observable which are asynchronous (does not wait for the expect block to run). You can use the done parameter to run after the expect has run. Now the test will wait for the subscribe to return then only close the test.

      it(`should set the length of output observable array as 1`, (done) => {
        const fixture = TestBed.createComponent(AppComponent);
        const app = fixture.componentInstance;
        const list = [{"id":"3","name":"Apple"},{"id":"5","name":"Samsung"}];
        listService.getList = (id: any) => {
            const filtered = list.filter((item: any) => item.id === id);
            return of(filtered);
        } as any;
        app.search = new BehaviorSubject('3');
        app.ngOnInit(); // <- doing again to reinitialize the listItems property with the mocked values.
        app.listItems.subscribe(resp => {
          expect(resp.length).toBe(1);
          done();
        })
      });