简体   繁体   English

使用 Karma 和 Jasmine / Angular 8 并行进行多个 API 调用的服务单元测试

[英]Unit testing for service with multiple API calls in parallel using Karma and Jasmine / Angular 8

I am trying to write unit tests (Jasmine and Karma) for a service and a component whose aim is to perform two API calls to the Star Wars API (more info at https://swapi.co/ ) with two different URLS , to get people and starships data.我正在尝试为服务和组件编写单元测试(Jasmine 和 Karma),其目的是使用两个不同的URLS对 Star Wars API(更多信息位于https://swapi.co/ )执行两个 API 调用,以获取人员和星舰数据。 Both calls are returned simultaneously with a forkJoin .两个调用都与forkJoin同时返回。

My App component calls the ApiService to perform two calls as follows:我的App组件调用ApiService来执行两个调用,如下所示:

APP COMPONENT:应用程序组件:

import {Component, OnInit} from '@angular/core';
import {ApiService} from '../services/api.service';

export class AppComponent implements OnInit {

  constructor(private apiService: ApiService) {}

onGetData() {
    this.apiService.getData().subscribe((data: any) => {

      this.peopleData = data[0];
      this.starshipData = data[1];

     });

  }

API SERVICE: API服务:

import {HttpClient} from '@angular/common/http';

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

  getData(): Observable<any[]> {
    const randomPeoplePage = Math.floor(Math.random() * 9) + 1;
    const peopleUrl = `https://swapi.co/api/people/?page=${randomPeoplePage}`;

    const randomStarshipsPage = Math.floor(Math.random() * 4) + 1;
    const starshipsUrl = `https://swapi.co/api/starships/?page=${randomStarshipsPage}`;

    const peopleProm = this.http.get(peopleUrl);
    const starshipProm = this.http.get(starshipsUrl);

    return forkJoin([peopleProm, starshipProm]);

  }
}

I am fairly inexperienced with testing, especially on how to test API calls properly.我对测试缺乏经验,尤其是在如何正确测试 API 调用方面。 I have been trying different solutions for the past couple of days, something like this:过去几天我一直在尝试不同的解决方案,如下所示:

 it('should return an Observable', () => {
    const randomPeoplePage = Math.floor(Math.random() * 9) + 1;
    const randomStarshipsPage = Math.floor(Math.random() * 4) + 1;


    const dummyData = [
      {
        count: 87,
        next: `https://swapi.co/api/people/?page=${randomPeoplePage + 1}` || null,
        previous: `https://swapi.co/api/people/?page=${randomPeoplePage - 1}` || null,
        results: new Array(87)
      },
      {
        count: 37,
        next: `https://swapi.co/api/starships/?page=${randomStarshipsPage + 1}` || null,
        previous: `https://swapi.co/api/starships/?page=${randomStarshipsPage - 1}` || null,
        results: new Array(37)
      }
    ];

    service.getData().subscribe(data => {
      expect(data.length).toBe(2);
      expect(data).toEqual(dummyData);

      const peopleReq = httpMock.expectOne(`https://swapi.co/api/people/?page=${randomPeoplePage}`);
      const starshipReq = httpMock.expectOne(`https://swapi.co/api/starships/?page=${randomStarshipsPage}`);

      forkJoin([peopleReq, starshipReq]).subscribe(() => {
        expect(peopleReq.request.method).toBe('GET');
        expect(starshipReq .request.method).toBe('GET');
      })

      peopleReq.flush(dummyData[0]);
      starshipReq.flush(dummyData[1]);

    });


  });
});

but this test isn't passing and I am really not sure why or what should I look into.但是这个测试没有通过,我真的不知道为什么或我应该调查什么。 Do I need to test the API calls in the api.service.spec.ts or in the app.component.spec.ts ?我是否需要在api.service.spec.tsapp.component.spec.ts测试 API 调用? How do I simulate the return?我如何模拟退货? Should I test them singularly?我应该单独测试它们吗? This is an 'academic' test, therefore any hint for best practice is welcome.这是一个“学术”测试,因此欢迎任何有关最佳实践的提示。

Thanks a lot!非常感谢!

A component should contain logic that is concerned with the presentation of the data.组件应包含与数据表示相关的逻辑 Now, you could have both Smart and Dumb components.现在,您可以同时拥有SmartDumb组件。
For Smart components, you could test the interaction with external services that provide data(store, route, a service that provides data), whereas for Dumb components you could test if the data is displayed properly and/or if @Output events are sent properly.对于Smart组件,您可以测试与提供数据的外部服务(存储、路由、提供数据的服务)的交互,而对于Dumb组件,您可以测试数据是否正确显示和/或@Output事件是否正确发送.

A service should contain business logic (api calls, maybe storing data etc...), logic that is not directly concerned with data presentation.服务应包含业务逻辑(api 调用,可能存储数据等),这些逻辑与数据呈现不直接相关。

In this case, I'd simply provide some predefined data for the getData() so I ensure that I won't call the real API.在这种情况下,我只需为getData()提供一些预定义的数据,以便确保我不会调用真正的API。

  it('....', () => {
    const url1 = 'url1';
    const url2 = 'url2';

    const responseMap = {
      url1: { data: 'url1' },
      url2: { data: 'url2' }
    }

    // Assuming this is imported..    
    class ApiService {
      constructor (private http) { }

      getData () {
      }
    }

    const getSpy = jasmine.createSpy('Http.get').and.callFake((arg) => {
      return of(responseMap[arg]);
    });
    const mockHttp = { get: getSpy }
    const apiService = new ApiService(mockHttp);

    spyOn(apiService, 'getData').and.callFake(function () {
      return forkJoin([this.http.get(url1), this.http.get(url2)])
    });

    apiService.getData()
      .subscribe(([r1, r2]) => {
        expect(getSpy).toHaveBeenCalledWith(url1);
        expect(getSpy).toHaveBeenCalledWith(url2);

        expect(r1).toBe(responseMap['url1']);
        expect(r2).toBe(responseMap['url2']);
      })
  });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM