Testing angular service with $httpBackend with async/await using jasmin

别等时光非礼了梦想. 提交于 2021-02-18 13:47:45

问题


I have previously successfully tested an angular controller that uses ES7 async/await syntax with jasmine -

async updateGridAsync() {
        const paging = angular.copy(this.gridData.getServerCallObj());            
        }
        try {
            const model = await this.service.getAsync(paging);
            this._$rootScope.$apply();
        } catch (e){this._notification.error(this._$rootScope.lang.notifications.unexpectedError);
        }
    }

it('updateGridAsync() should update the gridData when succeed', async (done) => {
    expect(ctrl.gridData.totalItems).toEqual(2);
    expect(ctrl.gridData.items.length).toEqual(2);
    spyOn(service, 'getAsync').and.callFake(() => {
        return Promise.resolve(responseStub);
    });
    await ctrl.updateGridAsync();
    expect(service.getAsync).toHaveBeenCalled();
    expect(ctrl.gridData.totalItems).toEqual(1);
    expect(ctrl.gridData.items.length).toEqual(1);
    done();
});

The above code works perfectly. However, I encountered a problem once trying to test the mocked service code, that calls $http.post. This is the code I run in the service:

async getAsync(pagingData, spinner, gridId, payeeId){            
        const response = await $http.post(url, params);
        const model = this.modelFactory(response.data);
        return model ;
    }

and the test method that isn't able to go a step after the await in updateGridService:

it('getAsync should return correct model', async (done) => {                
    $httpBackend.whenPOST(theUrl).respond(200, responseStub);

    const model = await service.getAsync();

    expect(model.list.length).toEqual(2);        
    $httpBackend.flush();
    done();
});

A few things to point out -

  • Before using async await, this test passed. Now, I don't get passed the await in the service.
  • The functionality works when not in testing context.
  • I'm using jasmine 2.4.1 and angularJS 1.6

回答1:


I don't think you can use those together - await is actually waiting till the backend promise resolves and that is triggered by flush, but also triggering it before any request has been made does no good.

I mean if you wanna use it in a straightforward way, I personally use it with a helper that creates timeout for the flush action and then awaits for the request.

export async function withHttp(
    callbackHttp: (http: HttpTestingController) => void,
    callbackTest: () => Promise<void>
): Promise<void> {
    const http = TestBed.get(HttpTestingController);
    setTimeout(() => callbackHttp(http));
    await callbackTest();
}

Also don't use async test methods together with done (and don't use done when it's just called on the last line of test, it's not needed then).




回答2:


Just ran into the same issue and I think there's no solution for it. Maybe upgrading to Angular 7 helps, hopefully the problem is fixed there. But that's not a quick solution.

Awaiting the asynchronous method that does the HTTP calls leads to a deadlock, because the HTTP calls don't return until $httpBackend.flush() is called.

Not awaiting the asynchronous method does not help either. Then the assertions after $httpBackend.flush() can fail because the framework does not wait long enough before continuing with assertions.

I really wonder how this "magic" is implemented. It seems that when the asynchronous method returns either void or IPromise<void> it works. Returning Promise<void> (or using async) does not work though.



来源:https://stackoverflow.com/questions/45606050/testing-angular-service-with-httpbackend-with-async-await-using-jasmin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!