【问题标题】:Angular unit test with fakeAsync and tick not waiting for code to execute with Observable使用 fakeAsync 进行角度单元测试,并且不等待代码使用 Observable 执行
【发布时间】:2022-01-24 18:03:06
【问题描述】:

在我的 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() 是否已从订阅中调用?

【问题讨论】:

  • 我会使用HttpClientTestingModule 来测试只进行http 调用的服务:angular-training-guide.rangle.io/testing/services/http/…
  • 是的。但是在文档中。当进行任何调用时,它只会在该测试用例中被订阅。就我而言,组件方法(post)不返回任何内容。那已经订阅了数据。那么,@AliF50 现在如何检查是否调用了成功方法?
  • 您可以刷新 API 调用的响应并查看调用了 completeSpyonComplete 是私有的,因此很难窥探到它。
  • @AliF50 我用flush检查了它不起作用,只是为了测试,我还将方法从私有更改为公共,但仍然不起作用。

标签: angular unit-testing jasmine karma-jasmine


【解决方案1】:

我会使用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(...);

【讨论】:

  • 谢谢@AliF50,我已经在环境文件中指定了url。所以,从 RestService 类,我直接访问环境。
  • 您的解决方案非常好。我在我的代码中发现了问题。 const subscrieSpy = spyOn(Observable.prototype, "subscribe"); 在这里进行间谍活动时,我必须添加 callThrough。 const subscrieSpy = spyOn(Observable.prototype, "subscribe").and.callThrough();。您能否将其添加到您的答案中。所以,我可以把它标记为答案。
  • 我已经完成了编辑,谢谢。
  • 你能告诉我我怎么能期望请求正文? @阿里F50
  • 谢谢。 @阿里F50
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多