I am trying to mock the PUT
call of HttpClient
of Angular to throw error. I am using throwError
for it. It isn't working. What should I change to make it throw the error and call the handleError
method? I am using Jest.
it(`should call the 'handleError' method when a request to store data was not successful`, () => {
const error: HttpErrorResponse = {
status: 401,
message: 'You are not logged in',
} as HttpErrorResponse;
jest.spyOn(httpClientServiceMock, 'put').mockReturnValue(throwError(error));
const spy = jest.spyOn(httpService, 'handleError');
httpService.requestCall('some-url', ApiMethod.PUT, {});
expect(spy).toBeCalled();
});
service file
requestCall(url: string, method: ApiMethod, data?: any): Observable<any> {
const headers = {
'X-XSRF-TOKEN': this.xsrfToken,
'Content-Type': 'application/json; charset=UTF-8',
};
const requestConfig = {
withCredentials: true,
headers,
};
switch (method) {
case ApiMethod.GET:
return this._http.get(url, { withCredentials: true });
case ApiMethod.PUT:
return this._http
.put(url, data, requestConfig)
.pipe(catchError((error) => this.handleError(error)));
}
}
handleError(error: HttpErrorResponse): any {
if (error.error instanceof ErrorEvent) {
console.error(`An error occurred: ${error.error.message}`);
}
return throwError({ error: error.message, status: error.status });
}
To properly make mock throw an error in Jest, we call the mockImplementation method and throw an error in the callback we call the method with. it("should throw error if email not found", async () => { callMethod . mockImplementation(() => { throw new Error("User not found [403]"); }) .
to use throw to thrown an error in the mocked implementation of yourMockInstance . If we're mocking async functions, we can use mockRejectedValue to mock the value of a rejected promise returned by the async function. test('async test', async () => { const yourMockFn = jest. fn().
To mock the return value of an imported function in Jest, you have to either call mockReturnValue or mockImplementation on a Jest mock function and then specify the return value. Which function mock function you should use depends on your situation.
You were pretty close!
You have to subscribe to observable returned from httpService.requestCall('some-url', ApiMethod.PUT, {})
function. There are additional changes required as this is asynchronous
const { of , throwError, operators: {
catchError
}
} = rxjs;
const httpClientServiceMock = {
put: () => of ({
value: 'test'
})
};
const httpService = {
requestCall(url, data, requestConfig) {
return httpClientServiceMock
.put(url, data, requestConfig)
.pipe(catchError((error) => this.handleError(error)));
},
handleError(error) {
return throwError({});
}
};
const ApiMethod = {
PUT: ''
}
const {
expect,
test,
run,
it,
describe,
jest
} = jestLite.core;
describe('httpService', () => {
it(`should call the 'handleError' method when a request to store data was not successful`, done => {
const error = {
status: 401,
message: 'You are not logged in',
}
jest.spyOn(httpClientServiceMock, 'put').mockReturnValue(throwError(error));
const spy = jest.spyOn(httpService, 'handleError');
httpService
.requestCall('some-url', ApiMethod.PUT, {})
.subscribe(pr => {
done.fail(new Error(`It shouldn't go this path!`))
}, error => {
expect(spy).toBeCalled();
done();
});
});
});
run().then(result => {
console.log(result[0]);
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
As already pointed out in the other answer, you have to subscribe to the returned observable.
I just wanted to add another approach which uses marble-testing, so you don't have to manually subscribe to that observable:
let testScheduler;
beforeEach(() => testScheduler = new TestScheduler(assertionFn))
it(`should call the 'handleError' method when a request to store data was not successful`, () => {
const error = {
status: 401,
message: 'You are not logged in',
} as HttpErrorResponse;
jest.spyOn(httpClientServiceMock, 'put').mockReturnValue(throwError(error));
const spy = jest.spyOn(httpService, 'handleError');
testScheduler.run(({ cold, expectObservable }) => {
const src$ = httpService.requestCall('some-url', ApiMethod.PUT, {});
expectObservable(src$).toBe('#', null, { error: error.message, status: error.status });
expect(spy).toBeCalled();
});
});
TestScheduler
is available in rxjs/testing
and the run
's callback provides several helpers, such as: cold
, hot
, flush
, expectObservable
, expectSubscriptions
and time
.
What I personally like about this is that everything is synchronous, so you might not have to call done()
when following such approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With