繁体   English   中英

Jasmine / Karma测试Angular 5 HTTP响应

[英]Jasmine/Karma testing Angular 5 HTTP responses

我对TDD完全陌生,我正在尝试调试公司中正在使用的Angular 5大应用程序。

该应用程序运行良好,但是现在我必须实施测试,并且在创建最基础和入门的软件时我正在学习这些东西。 我已经为主要模块编写了这些东西,只是为了尝试这个工具:

describe('AppComponent', () => {
  let httpClientSpy: { get: jasmine.Spy }
  let dataReq: DataRequester;
  let queryBuilder: PanelQueryBuilder;
  let query: DataQuery;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        MainComponent,
        Menu,
        MessageViewer
      ],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        routing,
        AngularFontAwesomeModule,
        FormsModule,
        HttpModule,
        ChartModule,
        Ng4LoadingSpinnerModule.forRoot(),
        NgbModule.forRoot()
      ],
      providers: [
        ReactiveService,
        DataRequester,
        { provide: APP_BASE_HREF, useValue : '/' }
      ]
    }).compileComponents();
  }));
  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(MainComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();

  }));
  it('userType should be defined', async(()=>{
    expect(MainComponent.userType).toBeDefined();
  }))

  it('DataRequester exists and retrieves info', async(()=>{


    beforeEach(() => {
      // TODO: spy on other methods too
      httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
      dataReq = new DataRequester(<any> httpClientSpy);
      queryBuilder = new PanelQueryBuilder();
    });



    expect(MainComponent).toBeDefined();
  }))
  it('1. Build query and check integrity', async()=>{
    query = queryBuilder.buildInitialQuery("panel", "conversions", 144);
    expect(query).toBeDefined();
  })

  it('2. Send query and check result', async()=>{
    dataReq.requestData(query, 'conversions').then( res => {
      expect(res).toContain("panel");

    })
  })
});

我希望您专注于一部分:DataRequester服务。 这是一种具有返回承诺的方法的服务,并调用后端的特定部分以返回数据。 我只想在这里检查此响应对象是否包含属性“ panel”以及测试...

……实际上说它存在! 但是,如果我尝试将属性的名称更改为某些不存在的属性,它也将验证为true。 因此,也许HTTP请求在这里不能正常工作,在这里做错了。

我在这段代码中做不好吗? 为什么DataRequester的“ requestData”方法不能正确执行,所以Jasmine能够在响应对象中正确测试我想要的条件?

是的,您在代码中占了上风。但是不用担心,刚开始时我也做过同样的事情。

首先,你必须了解单元测试的基础知识:单元测试旨在防止在一个单位 的副作用

副作用是所需行为的变化:例如,您要使div的颜色为蓝色,并且在进行一些代码编辑后,将其着色为红色:这是一个副作用。

单位是您要测试的功能。 在Angular中,您可以看到以下内容:

describe('AppComponent'

在这里,您正在测试AppComponent

现在,我们解决了这一点,让我们回顾一下测试中的错误:您使用服务的真实实例 这意味着您不再没有一个单元:您正在测试多个单元。

更正

您必须嘲笑您的服务。 您将检查组件是否实际上在调用服务,而不是服务是否在调用API(这将由服务本身的单元测试进行检查)。

在您的测试床上:

TestBed.configureTestingModule({
  declarations: [
    MainComponent,
    Menu,
    MessageViewer
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    routing,
    AngularFontAwesomeModule,
    FormsModule,
    HttpModule,
    ChartModule,
    Ng4LoadingSpinnerModule.forRoot(),
    NgbModule.forRoot()
  ],
  providers: [
    {
      provide: ReactiveService,
      useValue : {}
    },
    {
      provide: DataRequester,
      useValue: {}
    },
    { provide: APP_BASE_HREF, useValue : '/' }
  ]

通常,组件仅处理视图:您实际上并不能模拟它们(尽管您应该 )。

这允许您删除HttpModule ,这在任何测试中都是不需要的。

您还可以删除路由模块,因为Angular已经提供了它的模拟: RouterTestingModule

你的测试床变成这个

TestBed.configureTestingModule({
  declarations: [
    MainComponent,
    Menu,
    MessageViewer
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    RouterTestingModule,
    AngularFontAwesomeModule,
    FormsModule,
    ChartModule,
    Ng4LoadingSpinnerModule.forRoot(),
    NgbModule.forRoot()
  ],
  providers: [
    {
      provide: ReactiveService,
      useValue : {}
    },
    {
      provide: DataRequester,
      useValue: {}
    },
    { provide: APP_BASE_HREF, useValue : '/' }
  ]

现在,您已经有了合适的测试床。

您所要做的就是,在useValue中添加useValue并使用正确的签名在组件中使用的每个服务属性

例如,假设您的应用程序组件具有以下内容:

ngOnInit() {
  this.dataRequester.requestWidth('URL').subscribe(res => this.reactiveService.width = res);
}

然后,您的测试台将变为:

TestBed.configureTestingModule({
  declarations: [
    MainComponent,
    Menu,
    MessageViewer
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    RouterTestingModule,
    AngularFontAwesomeModule,
    FormsModule,
    ChartModule,
    Ng4LoadingSpinnerModule.forRoot(),
    NgbModule.forRoot()
  ],
  providers: [
    {
      provide: ReactiveService,
      useValue : {
        width: 0
      }
    },
    {
      provide: DataRequester,
      useValue: {
        requestWidth: () => of(100)
      }
    },
    { provide: APP_BASE_HREF, useValue : '/' }
  ]

(模拟的值并不重要,您可以根据需要更改它们)

如您所见,由于您的请求者服务返回了一个Observable,因此您也不得不返回一个。 并且由于您的反应式服务存储了宽度,因此您必须声明一个类型为number的变量。

现在,在测试中,使用前面的示例,您将执行以下操作:

it(`should get width from requester and store it in reactive service`, fakeAsync(() => {
  spyOn(component['dataRequester'], 'requestWidth').and.callThrough();

  component.ngOnInit();
  tick();

  expect(component['dataRequester'].requestWidth).toHaveBeenCalledWith('URL');
  expect(component['reactiveService'].width).toEqual(100);
}));

您声明所做的事情(测试驱动),监视服务(以查看是否已被调用),然后进行调用(因为我们的模拟已经是一个Observable并返回100)。

然后,调用该方法进行测试,然后刷新异步调用( fakeAsynctick是Angular的一部分,您可以在有关测试的文档中找到它们)。

最后,您会做出期望。

这样,您就成功地测试了第一种方法!

暂无
暂无

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

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