首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用“`TestHttpInterceptor`”测试角8服务?

用“`TestHttpInterceptor`”测试角8服务?
EN

Stack Overflow用户
提问于 2020-02-05 12:00:29
回答 1查看 106关注 0票数 0

我有一个相当小的角度前端,它从外部服务器获取大量的数据。

测试结果显示我的身体

我可以通过模拟服务来测试一个简单的组件,但是这是一个“替换输出”解决方案,而不是真正的测试.为此,我相信当服务调用外部API时,我需要提供一个已知的返回。

下面是一个简单的例子:

对象/接口定义:

代码语言:javascript
复制
// alerts.ts
export interface Alert {
  id: number;
  display_message: string;
  is_enabled: boolean;
}

服务定义:

代码语言:javascript
复制
// alerts.service.ts
import { of as observableOf, Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Alert } from './alerts';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

export interface IAlertService {
  getAlerts(): Observable<Alert[] | null>;
}

@Injectable()
export class AlertService implements IAlertService {

  readonly baseUrl = '/api/alerts/';
  alerts$: Observable<Alert[] | null>;

  constructor(private _http: HttpClient) { }

  getAlerts(): Observable<Alert[] | null> {
    return this._http.get<Alert[] | null>(this.baseUrl).pipe(
      catchError(error => {
        console.log('in catch: ', error);
        return observableOf(null);
      }));
  }
}

组件代码:

代码语言:javascript
复制
// alerts/alerts.component.html
<div *ngIf="alerts" >
  <div class="service-notice" *ngFor="let alert of alerts">
    <p [innerHTML]="alert.display_message"></p>
  </div>
</div>

代码语言:javascript
复制
// alerts/alerts.component.ts
import { Component, OnInit } from '@angular/core';

import { Alert } from '../alerts';
import { AlertService } from '../alerts.service';

@Component({
  selector: 'naas-alerts',
  templateUrl: './alerts.component.html',
  styleUrls: ['./alerts.component.scss'],
})
export class AlertComponent implements OnInit {

  alerts: Alert[] | null;

  constructor(private alertService: AlertService) { }

  ngOnInit() {
    this.alertService.getAlerts().subscribe(data => {
      this.alerts = data;
    });
  }
}

然后,我编写了一个httpInterceptor类,以便定义我希望在测试中返回的字符串:

代码语言:javascript
复制
// testing_interceptors.ts
import {
    HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, HTTP_INTERCEPTORS
  } from '@angular/common/http';
import { Injectable, Injector } from '@angular/core';
import { of as observableOf, Observable } from 'rxjs';

@Injectable()
export class TestHttpInterceptor implements HttpInterceptor {

  current_containers: string = '[]';
  current_user: string = '';
  alerts: string = '[]';

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    console.log('TestHttpInterceptor called');
    const url_regex = /(\/api(?:\/[\w+\/]+)+)$/;
    const url_path = url_regex.exec(request.url)[1];

    if(request.method === "GET") {
        if(url_path == '/api/alerts/') {
            return observableOf(new HttpResponse({
                status: 200,
                body: this.alerts
            }));
        }
        if(url_path == '/api/users/current/') {
            return observableOf(new HttpResponse({
                status: 200,
                body: this.current_user
            }));
        }
        if (url_path === '/api/users/current/containers/') {
            return observableOf(new HttpResponse({
                status: 200,
                body: this.current_containers
            }));
        }
    }
  }
}

..。我的测试(是的,我还有一些旧的_component_mock_注释掉了):

代码语言:javascript
复制
// alerts/alerts.component.spec.test
import { HttpClientModule, HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
import { TestBed, ComponentFixture } from '@angular/core/testing';

import { AlertComponent } from './alerts.component';
import { AlertService } from '../alerts.service';
import { AlertServiceMock } from '../alerts.service.mock';

import { TestHttpInterceptor } from '../testing_interceptors';

describe('AlertsComponent', () => {
  let fixture: ComponentFixture<AlertComponent>;
  let component: AlertComponent;
  // let alertServiceMock: AlertServiceMock;
  let testHttpInterceptor: TestHttpInterceptor;

  beforeEach(() => {
    // alertServiceMock = new AlertServiceMock();
    testHttpInterceptor = new TestHttpInterceptor();

    TestBed.configureTestingModule({
      declarations: [ AlertComponent ],
      providers: [
        {provide: AlertService, useClass: AlertService },
        {provide: HttpClient, useClass: HttpClientModule},
        {provide: HTTP_INTERCEPTORS, useClass: TestHttpInterceptor, multi: true }
      ],
    }).compileComponents();
    fixture = TestBed.createComponent(AlertComponent);
    component = fixture.componentInstance;
  });

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

  // it('should have no alerts with no data', () => {
  //   alertServiceMock.test_alert = null;
  //   fixture.detectChanges();
  //   const compiled = fixture.debugElement.queryAll(By.css('p'));
  //   expect(compiled.length).toBe(0);
  // });

  // it('should have one alert', () => {
  //   alertServiceMock.test_alert = [{
  //     id: 1,
  //     display_message: 'Foo',
  //     is_enabled: true,
  //   }];
  //   fixture.detectChanges();
  //   const compiled = fixture.debugElement.queryAll(By.css('p'));
  //   expect(compiled.length).toBe(1);
  // });
});

问题是,当我运行这个程序时,我会得到以下错误:

代码语言:javascript
复制
    TypeError: this.http.get is not a function
    error properties: Object({ ngDebugContext: DebugContext_({ view: Object({ def: Object({ factory: Function, nodeFlags: 33669121, rootNodeFlags: 33554433, nodeMatchedQueries: 0, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 33554433, childFlags: 114688, directChildFlags: 114688, childMatchedQueries: 0, matchedQueries: Object({  }), matchedQueryIds: 0, references: Object({  }), ngContentIndex: null, childCount: 1, bindings: [  ], bindingFlags: 0, outputs: [  ], element: Object({ ns: '', name: 'naas-alerts', attrs: [  ], template: null, componentProvider: Object({ nodeIndex: 1, parent: <circular reference: Object>, renderParent: <circular reference: Object>, bindingIndex: 0, outputIndex: 0, checkIndex: 1, flags: 114688, childFlags: 0, directChildFlags: 0, childMatchedQueries: 0, matchedQueries: Object, matchedQueryIds: 0, references: Object, ngContentIndex: -1, childCount: 0, bindings: Array, bindingFlags: 0, outputs: Array ...
        at <Jasmine>
        at AlertService.getAlerts (http://localhost:9876/_karma_webpack_/src/app/alerts.service.ts:20:22)
        at AlertComponent.ngOnInit (http://localhost:9876/_karma_webpack_/src/app/alerts/alerts.component.ts:18:23)
        at checkAndUpdateDirectiveInline (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:31910:1)
        at checkAndUpdateNodeInline (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44367:1)
        at checkAndUpdateNode (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44306:1)
        at debugCheckAndUpdateNode (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45328:36)
        at debugCheckDirectivesFn (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45271:1)
        at Object.eval [as updateDirectives] (ng:///DynamicTestModule/AlertComponent_Host.ngfactory.js:10:5)
        at Object.debugUpdateDirectives [as updateDirectives] (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45259:1)
        at checkAndUpdateView (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44271:1)
Chrome 80.0.3987 (Linux 0.0.0): Executed 16 of 23 (1 FAILED) (0 secs / 2.133 secs)
Chrome 80.0.3987 (Linux 0.0.0) AlertsComponent should be created FAILED
    TypeError: this.http.get is not a function
    error properties: Object({ ngDebugContext: DebugContext_({ view: Object({ def: Object({ factory: Function, nodeFlags: 33669121, rootNodeFlags: 33554433, nodeMatchedQueries: 0, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 33554433, childFlags: 114688, directChildFlags: 114688, childMatchedQueries: 0, matchedQueries: Object({  }), matchedQueryIds: 0, references: Object({  }), ngContentIndex: null, childCount: 1, bindings: [  ], bindingFlags: 0, outputs: [  ], element: Object({ ns: '', name: 'naas-alerts', attrs: [  ], template: null, componentProvider: Object({ nodeIndex: 1, parent: <circular reference: Object>, renderParent: <circular reference: Object>, bindingIndex: 0, outputIndex: 0, checkIndex: 1, flags: 114688, childFlags: 0, directChildFlags: 0, childMatchedQueries: 0, matchedQueries: Object, matchedQueryIds: 0, references: Object, ngContentIndex: -1, childCount: 0, bindings: Array, bindingFlags: 0, outputs: Array ...
        at <Jasmine>
        at AlertService.getAlerts (http://localhost:9876/_karma_webpack_/src/app/alerts.service.ts:20:22)
        at AlertComponent.ngOnInit (http://localhost:9876/_karma_webpack_/src/app/alerts/alerts.component.ts:18:23)
        at checkAndUpdateDirectiveInline (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:31910:1)
        at checkAndUpdateNodeInline (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44367:1)
        at checkAndUpdateNode (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44306:1)
        at debugCheckAndUpdateNode (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45328:36)
        at debugCheckDirectivesFn (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45271:1)
        at Object.eval [as updateDirectives] (ng:///DynamicTestModule/AlertComponent_Host.ngfactory.js:10:5)
        at Object.debugUpdateDirectives [as updateDirectives] (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45259:1)
Chrome 80.0.3987 (Linux 0.0.0): Executed 23 of 23 (1 FAILED) (2.504 secs / 2.346 secs)

..。我周游了T‘’Internet,在写拦截器时发现了很多东西.但对他们的测试却更少。

我在这件事上花了太长时间,可以接受建议。

  1. 这实际上是一个有效的测试解决方案(我查看了marbles,但发现更少的marbles可以让它工作吗?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-02-07 09:04:45

这就是对我有用的..。

我特别想测试组件生成的html,对于来自外部API调用的给定响应,我希望尽可能多地测试alerts.component.ts中的代码。

代码语言:javascript
复制
// alerts.component.spce.ts
import { By } from '@angular/platform-browser';
import {
  HttpClientTestingModule,
  HttpTestingController,
} from '@angular/common/http/testing';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { Type } from '@angular/core';

import { AlertComponent } from './alerts.component';
import { AlertService } from '../alerts.service';

/*

This tests alerts.component.ts.

AlertComponent has an ngOnInit method, which uses AlertService.getAlerts

AlertService.getAlerts calls `/api/alerts/`
.... HOWEVER the HttpTestingController catches it & flushes back our canned
reponse.

This response is processed by AlertService.getAlerts & returned to
AlertComponent - which builds the fragment of html defined in
alerts.components.html - which we can test.

*/

describe('AlertsComponent', () => {
  let fixture: ComponentFixture<AlertComponent>;
  let httpMock: HttpTestingController;
  let alertComponent: AlertComponent;

  beforeEach( async () => {

    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      declarations: [ AlertComponent ],
      providers: [ AlertService ],
    });

    await TestBed.compileComponents();

    fixture = TestBed.createComponent(AlertComponent);
    alertComponent = fixture.componentInstance;
    httpMock = fixture.debugElement.injector
      .get<HttpTestingController>(HttpTestingController as Type<HttpTestingController>);

    fixture.detectChanges();

  });

  afterEach(() => {
    httpMock.verify();
  });

  it('should be created', () => {
    alertComponent.ngOnInit();
    const reqs = httpMock.match(`/api/alerts/`);
    for (const req of reqs) {
      req.flush(null);
    }
    fixture.detectChanges();
    expect(alertComponent).toBeTruthy();
  });

  it('The component should init, call the alert service, and get a response', () => {
    const dummyAlerts = [{
      id: 1,
      display_message: 'Foo',
      is_enabled: true,
    }];

    alertComponent.ngOnInit();
    const reqs = httpMock.match(`/api/alerts/`);
    for (const req of reqs) {
      req.flush(dummyAlerts);
    }
    fixture.detectChanges();
    const compiled = fixture.debugElement.queryAll(By.css('p'));
    expect(compiled.length).toBe(1);
  });

  it('The component should build 2 alerts from a response', () => {
    const dummyAlerts = [{
      id: 1,
      display_message: 'Foo',
      is_enabled: true,
    }, {
      id: 2,
      display_message: 'Bar',
      is_enabled: true,
    }];

    alertComponent.ngOnInit();
    const reqs = httpMock.match(`/api/alerts/`);
    for (const req of reqs) {
      req.flush(dummyAlerts);
    }
    fixture.detectChanges();
    const compiled = fixture.debugElement.queryAll(By.css('p'));
    expect(compiled.length).toBe(2);
  });

  // ## This fails when looking for 'small' - needs investigated ##
  it('The component should build 2 alerts from a response', () => {
    const dummyAlerts = [{
      id: 1,
      display_message: '<small>Foo</small>',
      is_enabled: true,
    }];

    alertComponent.ngOnInit();
    const reqs = httpMock.match(`/api/alerts/`);
    for (const req of reqs) {
      req.flush(dummyAlerts);
    }
    fixture.detectChanges();
    const compiled = fixture.debugElement.queryAll(By.css('p'));
    expect(compiled.length).toBe(1);
  });
});

(我很高兴被告知更好的方法)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60075364

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档