模拟对象如何在Angular jasmine单元测试中工作

时间:2018-04-20 13:06:38

标签: angular angular-httpclient

我正在学习单元测试和Angular,所以我是初学者。 我已经提到了几个关于单元测试http的角度的教程和文章。

我无法理解httpMock使用HttpTestingController做了什么。我们称实际服务的功能然后我们称之为模拟? 什么是潜在的过程? 请参考一些文章以便更好地理解。

提前致谢。

修改 这是issue,我坚持使用httpMock。

3 个答案:

答案 0 :(得分:4)

我们以我的档案为例,好吗?

import { SimpleConfiguration } from '../../../../../model/SimpleConfiguration';
import { SessionService } from '../../../session/session.service';
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { ConfigurationService } from './configuration.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

describe('ConfigurationService', () => {

  let httpMock: HttpTestingController;
  let service: ConfigurationService;

  const createFakeFile = (fileName: string = 'fileName'): File => {
    const blob = new Blob([''], { type: 'text/html' });
    blob['lastModifiedDate'] = '';
    blob['name'] = fileName;
    return <File>blob;
  };

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule,
        HttpClientTestingModule
      ],
      providers: [ConfigurationService, SessionService]
    });

    httpMock = TestBed.get(HttpTestingController);
    service = TestBed.get(ConfigurationService);
  });

  it('should be created', done => {
    expect(service).toBeTruthy();
    done();
  });

  it('getConfigurations should GET on postes-sources/:postID/configuration', (done) => {
    service.getConfigurations(0).subscribe(res => done());
    const successRequest = httpMock.expectOne(service.URL + 'postes-sources/0/configurations');
    expect(successRequest.request.method).toEqual('GET');
    successRequest.flush(null);
    httpMock.verify();
  });

  it('uploadFile should POST on postes-sources/:postID/configuration', (done) => {
    service.uploadFile(0, createFakeFile(), new SimpleConfiguration()).subscribe(res => done());
    const successRequest = httpMock.expectOne(service.URL + 'postes-sources/0/configurations');
    expect(successRequest.request.method).toEqual('POST');
    successRequest.flush(null);
    httpMock.verify();
  });

  it('updateComment should POST on postes-sources/:postID/configurations/:confID', (done) => {
    service.updateConfiguration(0, 0, new SimpleConfiguration()).subscribe(res => done());
    const successRequest = httpMock.expectOne(service.URL + 'postes-sources/0/configurations/0');
    expect(successRequest.request.method).toEqual('POST');
    successRequest.flush(null);
    httpMock.verify();
  });

  it('getComponentInformations should GET on postes-sources/:postID/tranches/:trancheID/parametres', (done) => {
    service.getComponentInformations(0, 0).subscribe(res => done());
    const successRequest = httpMock.expectOne(service.URL + 'postes-sources/0/tranches/0/parametres');
    expect(successRequest.request.method).toEqual('GET');
    successRequest.flush(null);
    httpMock.verify();
  });
});

让我一步一步向你详细解释。

  1. 我们从describe开始我们的测试。它允许我们对测试进行分组。在这种情况下,我们按功能对测试进行分组,我们的功能是名为 ConfigurationService 的服务。然后我们提供一个回调函数:这是由Jasmine运行以运行测试的函数。

  2. 接下来,我们声明我们的变量。在这里,我们声明了2个变量和一个函数:httpMockservicecreateFakeFile()。这些变量在整个测试组中都会有所帮助,因此我们将它们声明为顶层。

  3. 然后是beforeEach:在每次测试之前,都会运行此函数来执行某些操作。在这一个中,它将创建一个 TestBed :这是一些样板代码,它将创建某种Angular模块,以允许您的测试运行,就像您的功能在真正的Angular应用程序中一样。

  4. 在此测试台中,您需要声明依赖项:因为我正在测试HTTP服务,所以我必须导入HTTP测试模块,并且因为我的测试使用路由,所以还必须导入路由器测试模块。我也需要导入正在测试的服务,并导入SessionService,因为我也在我的服务中使用它。

    之后,我通过TestBed.get 获取这些依赖项的服务实例:这将允许我监视其属性,并查看其值以及是否已调用它们。这也允许我调用我想测试的函数。

    1. 首次测试。第一个非常简单,默认情况下实现:我测试服务是否正确创建。如果没有,则意味着您缺少依赖关系,或者您嘲笑您的依赖关系错误。 expect(service).toBeTruthy()是真正的考验:期待,jasmine期望(duh)服务是真实的(即它不应该等于undefined或null) 。

    2. 下一个测试是一个真正的测试:在这个测试期间,我希望我的函数能够使用某个动词来调用某个端点。

    3. 我首先说,一旦拨打电话,我必须结束测试。这是通过调用done来完成的,这是上面给出的回调。

      然后,我模拟一个HTTP调用:这意味着我让Angular相信我正在进行HTTP调用,但我实际上是假装。但在它看来,它就像制作一个真实的:这就是模拟:你模拟行为或价值。如您所见,我在特定端点上模拟HTTP调用,并且我期望一个特定的HTTP谓词。

      最后,如果我的函数getConfigurations GETS在该URL上,我的测试将成功,如果没有,它将失败。这意味着如果我在实际服务中更改了端点,则测试将失败:此测试可防止副作用

      其他测试与此相同,所以我不需要解释它们。

      如果你想知道,我没有单独完成,就像你我寻求帮助并遵循教程一样。但是一旦你习惯它,它就会变得非常快速和容易地测试你的功能。

      我希望这会有所帮助,并随时提出您想要我解释的任何问题!

答案 1 :(得分:1)

模拟服务的想法是您不需要使用实际功能(真正的http调用),但您确实需要服务方法来运行您的代码而没有任何例外

例如: 你有一个组件,它在某些时候通过Http从某些API收集数据。 其中一个单元测试应该测试你的组件是否打了那个电话,但是如果那里有一个真正的电话你就不会给出一个该死的。单元测试的重点是测试呼叫是否已经发生。

编辑: 如果某些内部逻辑进行调用以收集数据以显示某些内容,则会发生同样的情况。你应该模拟http调用并返回你自己的数据。你的单元测试不应该来自外部。想象一下,您的测试将在没有互联网的环境中运行。测试应该随时通过。

无论您测试哪种服务,此方案都适用。单元测试应该具有单一的责任感。他们不应该依赖于与他们的主要目的不同的东西。

答案 2 :(得分:1)

我实际上刚刚在过去一周遇到过这个问题,所以这在我脑海中非常新鲜。 Jasmine对我有点困惑,所以我可以分享我为解决问题所做的工作。

首先,Angular教程会误导新测试人员。它声称我们应该使用Jasmine,但后来开始使用TestBed,这有点误导。我最终选择了TestBed,我可以告诉你我用过的东西。

所以你有你的描述:

descripe('randomService', () -> {

}

您需要初始化`randomService':

descripe('randomService', () -> {
    let randomService: RandomService;         
}

使用beforeEach(),您可以在描述中的每个it语句之前重新初始化,赋值等。

descripe('randomService', () -> {
   let randomService: RandomService;         
   beforeEach(() => {
       imports: [
           HttpClientTestingModule
       ]
   });
}

所以我告诉Angular在每个HttpClientTestingModule块之前重新导入it。我的randomService需要HttpClient所以我需要创建一个Jasmine Spy对象,它返回我告诉它的内容,而不是让我的randomService命中实际的后端并改变后端的实际数据。

descripe('randomService', () -> {
   let randomService: RandomService;
   httpClientSpy;

   beforeEach(() => {
       imports: [
           HttpClientTestingModule
       ]
   });
   httpClientSpy = jasmine.CreateSpyObj('Http', ['get']);
   randomService = new RandomService(<any> httpclientSpy);       
}

所以现在每当我在randomService中使用'get'方法时,它就会真正使用httpClientSpy,并且它会编译,因为我告诉randomService我的参数类型为'any',并且它的'最佳知识,即实际上是一个真正的HttpClient,即使它不是。要正确使用此功能,您必须为假冒产品设置假回报:

descripe('randomService', () -> {
   let randomService: RandomService;
   httpClientSpy;
   mockResponse = { 1: ['first', 'second', 'third'] };
   beforeEach(() => {
       imports: [
           HttpClientTestingModule
       ]
   });
   httpClientSpy = jasmine.CreateSpyObj('Http', ['get']);
   randomService = new RandomService(<any> httpclientSpy);       
});

    it('should return first, second, third', () => {
        spyOn(httpClientSpy, 'get').and.returnValue(Observable.of(mockResponse));
        // randomService. <your get method here...>
        randomService.getValue().subscribe((response) =>
            expect(resposne[0].length).toEqual(3);
};
});

并且该响应应该是在我们的beforeEach()中创建的mockResponse。它不必位于beforeEach()中,但在此示例中,我将其保留在那里。