如何在 Angular2 中使用 MockBackend 对 Http post、put、delete 进行单元测试?

How to do a unit test for Http post,put,delete using MockBackend in Angular2?

所以我正在测试我的 angular2 组件和服务。

到目前为止,我已经使用 mockBackend 在服务中模拟 get 请求,如下所示:

/* tslint:disable:no-unused-variable */

import { MockBackend } from '@angular/http/testing';
import { Http, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions } from '@angular/http';
import { PagesService } from './pages.service';
import { tick, fakeAsync } from '@angular/core/testing/fake_async';
import { inject, TestBed } from '@angular/core/testing/test_bed';
import {GlobalService} from './../../shared/global.service';

describe('PagesService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        {
          provide: Http, useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) => {
            return new Http(backend, defaultOptions);
          }, deps: [MockBackend, BaseRequestOptions]
        },
        { provide: PagesService, useClass: PagesService },
        { provide: GlobalService, useClass: GlobalService },
        { provide: MockBackend, useClass: MockBackend },
        { provide: BaseRequestOptions, useClass: BaseRequestOptions }
      ]
    });
  });

  //should retrive all search results
  it('should retrieve all search results',
    inject([PagesService, MockBackend], fakeAsync((pagesService: PagesService, mockBackend: MockBackend) => {
      let res: Response;
      mockBackend.connections.subscribe(c => {
        expect(c.request.url).toBe('http://localhost:3200/pm/pages/');
        let response = new ResponseOptions({
          body: '[{"name": "Rapid"}, {"name": "NGBC"}]'});
          c.mockRespond(new Response(response));
        });
        pagesService.getAllpages().subscribe((response) => {
           res = response;              
        });
        tick();
        expect(res[0].name).toBe('Rapid');
     }))
  );
 it('should fetch by Page id',
  inject([PagesService, MockBackend], fakeAsync((pagesService: PagesService, mockBackend: MockBackend) => {
    let res;
    mockBackend.connections.subscribe(c => {
      let page_id:string='2';
      expect(c.request.url).toBe('http://localhost:3200/pm/pages/'+page_id);
      let response = new ResponseOptions({body: '[{"id": 1, "name": "Rapid"}, {"id": 2, "name": "NGBC"}]'});
      c.mockRespond(new Response(response));
    });
    pagesService.getPageById('2').subscribe((response) => {
      res = response;
   });
   tick();
   expect(res[1].name).toBe('NGBC');
   }))
 ); 
});

如果你想看我的 service.ts 如下:

export class PagesService {
    private headers = new Headers();
    public baseUrl:string;
    constructor(private http: Http,private _globalService:GlobalService) {
        this.baseUrl=this._globalService.baseUrl;
    }
    getAllpages() {
        return this.http.get(this.baseUrl + '/pm/pages/')
            .map((res: Response) => res.json()).catch(this.handleError);
    }

    getPageById(page_id: string) {
        return this.http.get(this.baseUrl + '/pm/pages/' + page_id)
            .map((res: Response) => res.json()).catch(this.handleError);
    }

    savePage(page: Object) {
        this.headers=new Headers();
        this.headers.append('Content-Type', 'application/json');
        let url = this.baseUrl+'/pm/pages/';
        let data={};
        data["data"]=page;
        return this.http.post(url, JSON.stringify(data),{headers: this.headers})
           .map((res: Response) => res.json()).catch(this.handleError);
    }

    updatePage(page: any) {
        this.headers=new Headers();
        this.headers.append('Content-Type', 'application/json');
        let url = this.baseUrl+'/pm/pages/'+page._id;
        let data={};
        data["data"]=page;
        return this.http.put(url, JSON.stringify(data),{headers: this.headers})
            .map((res: Response) => res).catch(this.handleError);
    }

    deletePage(page_id: string) {
         this.headers=new Headers();
         this.headers.append('Content-Type', 'application/json');
         let url = this.baseUrl+'/pm/pages/';
         return this.http.delete(url + page_id,{headers: this.headers,body: ''})
             .map((res: Response) => res).catch(this.handleError);
    }

    mergePage(page: Object) {
        return this.http.post(this.baseUrl + '/pm/pages/',JSON.stringify(page))
            .map((res: Response) => res.json()).catch(this.handleError);
    }

    handleError(error: any) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

通过上述服务,我已经在我的 service.spec.ts 文件中正确实施了 getAllPages() 和 getPageById() 方法,因为它们工作正常。

我想知道如何为 savePage() , updatePage() , deletePage() 方法实现测试用例。

有任何输入吗?

提前致谢。

您需要检查这些方法的内部结构,看看它在做什么。这就是您测试其预期行为的方式。

  • 您正在设置 Content-Type header。所以这是一项要求,您应该测试此行为。您可以从 MockConnection#request

    中获取 headers
    backend.connections.subscribe((conn: MockConnection) => {
      let contentType = conn.request.headers.get('Content-Type');
      expect(contentType).not.toBeNull();
      expect(contentType).toEqual('application/json');
    });
    
  • 您正在设置 URL,因此这是一项要求。这应该进行测试。你已经

    expect(conn.request.url).toBe('...');
    
  • 您正在发送数据。所以这是一个要求。您应该进行测试以确保数据符合要求 format/structure

    let body = conn.request.json();
    expect(body.data).toEqual('page');
    

这是请求部分。

对于响应部分,您应该测试您的服务对该响应执行的操作。

  • 如果您收到 body,您应该像处理 GET 请求一样测试它。

  • 您也在使用错误回调处理错误。您不需要在这里测试回调的作用。那应该是它自己的测试。我们想要测试的只是错误句柄是从被测试的方法中调用的。为此,我们应该使用间谍。我们可以窥探 handleError 函数,然后检查它是否已被调用。

    spyOn(service, 'handleError').and.callFake(() => {});
    // make call
    tick();
    expect(service.handleError).toHaveBeenCalled();
    

    我找不到在您的 Http 请求上调用 catch 方法的情况(使用 MockConnection),这就是我没有 post 示例的原因。我试过conn.mockError(new Error()),没用。我尝试在连接中抛出错误,但没有用。也许你能想出来。