Angular 使用 fakeAsync 和 tick 进行单元测试,不等待使用 Observable 执行代码

Angular unit test with fakeAsync and tick not waiting for code to execute with Observable

在我的 Angular 应用程序中,我正在尝试编写一个单元测试用例,其中我将其余服务响应模拟为 Observable。因此,我在测试用例中使用了 fakeAsync & tick。但是还是失败了。

service.ts

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

@Injectable()
export class RestServices{

    constructor(private _http: HttpClient){}

    public post(body: any): void{
        // have variables initializations

        this._http
            .post(url, body, { observe: "response" })
            .subscribe(
                response => this._onSuccess(response),
                error => this._onFailure(error);
            );
    } 

    private _onSuccess(response: any){
        if(response.status == 200){
            // extract data from response
        }
    }

    private _onFailure(error: any){
        // code for failure
    }
}

service.spec.ts

import { TestBed, tick } from "@angular/core/testing";
import { of } from "rxjs/internal/observable/of";
import { Observable } from "rxjs/internal/Observable";


describe("Rest", () => {
    let _service: RestServices;

    beforeEach(() => {
        const httpSpyObject = jasmine.createSpyObj("HttpClient", {
            get: of(),
            post: of(new HttpResponse({ status: 200, body: "Text"})),
            put: of(),
        });
        TestBed.configureTestingModule({
            providers: [
                RestServices,
                { provide: HttpClient, useValue: httpSpyObject }
            ],
        });
        _service = TestBed.inject(RestServices);
    });

    it("should create", () => {
        expect(_service).toBeTruthy();
    });

   
    it("should call Success method on success response", fakeAsync(() => {
        const completeSpy = spyOn<any>(_service, "_onSuccess");
        const errorSpy = spyOn<any>(_service, "_onFailure");
        const subscrieSpy = spyOn(Observable.prototype, "subscribe");

        const body = { name: "abc" };

        _service.post(body);
        
        tick(1000);

        // Passing the below test case.
        expect(subscrieSpy).toHaveBeenCalled();

        // not passing below test case
        expect(completeSpy).toHaveBeenCalled();
    }));
});

现在,我如何通过第二个测试用例来检查 _onSuccess() 是否已从订阅调用?

我会使用 HttpClientTesingModule,它会使测试更容易。

describe('Rest', () => {
  let service: RestService;
  let httpTestingController: HttpTestingController;
  
  beforeEach(() => {
    TestBed.configureTestingModule({
       imports: [HttpClientTestingModule],
       providers: [RestService],
    });
    service = Testbed.inject(RestService);
    httpTestingController = TestBed.inject(HttpTestingController);
  });

  afterEach(() => {
    // ensure all http calls are dealt with (none in queue)
    httpTestingController.verify();
  });

  it('creates', () => {
    expect(service).toBeTruthy();
  });

  it('should call success method on success response', () => {
    const successSpy = spyOn(service, 'onSuccess');
    // 2nd argument is the body
    service.post('urlGoesHere', {});

    const request = httpTestingController.expectOne('urlGoesHere');
    expect(request.request.method).toBe('Post');
    
    // send this for the response
    req.flush({});
    expect(successSpy).toHaveBeenCalled();
  });
 
});

查看以上内容,希望它能帮助您入门。

我有一个问题要问你,post 方法需要一个主体但不需要 URL?我认为它也应该 url。

编辑:

您可能需要添加 .and.callThrough() 来调用您正在监视的内容的实际实现。

像这样:

const subscrieSpy = spyOn(Observable.prototype, "subscribe").and.callThrough();

编辑2:

let responseBody: any;
// at the beginning
// assign responseBody to response when onSuccess is called
spyOn(service, 'onSuccess').and.callFake(response => responseBody = response);
// after req.flush({});
expect(responseBody).toEqual(...);