- Published on
How to Unit Test Error Response Handling in Angular
- Authors
- Name
Photo by James Harrison on Unsplash
In this post we’ll quickly see how to mock an error response in order to test that an HttpInterceptor
is redirecting to a login page when a 401
status code is returned.
Testing the Redirect
First, in order to test a redirect, we create a redirect
method:
redirect(href: string) {
// don't redirect if localhost:9876 (for unit tests)
if (window.location.href.indexOf('http://localhost:9876') === -1) {
window.location.href = href;
}
}
This is so that we can monitor when a redirect is requested and also not actually redirect during the unit test. You would need to change the port to whatever you are using, but 9876
is the default for Karma.
Testing for the Error Response
Now we need to setup the beforeEach
for the tests:
const redirectFunctionName = 'redirect'
const user = getTestUser()
let httpTestingController: HttpTestingController
let httpClient: HttpClient
let appConfig: AppConfig
let globalErrorHandlerService: GlobalErrorHandlerService
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
AuthInterceptor,
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: APP_CONFIG, useValue: { apiHost: 'https://mockhost/api' } },
provideMockStore({
initialState: { user: { currentUser: user } },
}),
],
})
httpTestingController = TestBed.inject(HttpTestingController)
httpClient = TestBed.inject(HttpClient)
appConfig = TestBed.inject(APP_CONFIG)
globalErrorHandlerService = TestBed.inject(GlobalErrorHandlerService)
})
I’ve covered most of this code in detail in a previous post. The only significant change here is the injecting of the GlobalErrorHandlerService
, which is just the service where I put the redirect
method.
Now we implement the test to see if the redirect
method is called when a 401
response is received.
First we arrange the test by setting the URL and a creating a spy for the GlobalErrorHandlerService
, so that we can check whether or not the redirect
method was called.
it('should redirect to login when 401 response received', () => {
//arrange
const url = `${appConfig.apiHost}/mockendpoint`
const redirectSpy = spyOn(globalErrorHandlerService, redirectFunctionName)
//act
//assert
})
Next we perform the action to be tested. We make the call using httpClient.get
, but it’s not actually going anywhere across the wire because we imported the HttpClientTestingModule
, so that all HTTP requests will hit the HttpClientTestingBackend
.
//act
httpClient.get(url).subscribe()
Now comes the important part and the crux of this post, which is simulating the error response.
By calling httpTestingController.expectOne(url)
, we are doing two things. We are getting the mock test request while also setting the expectation that the (mock) call will be made exactly one time. If it is not made exactly one time the test will fail.
const mockRequest = httpTestingController.expectOne(url);
Now that we have the mock test request we can simulate a response. This can be done with the flush
method or in our case, the error
method, since the error is all we are interested in.
The error
method expects a ProgressEvent
which takes a type
argument which is not important for our purposes. We’re interested in the second argument to the error
method which is of type TestRequestOptions
. In those options we can specify that the status of the response is 401
.
mockRequest.error(new ProgressEvent('error'), { status: 401 });
Finally, we assert using our spy that the redirect
method was called with the URL to the login page, which is where we want to direct our users when we receive a 401
.
// assert
expect(redirectSpy).toHaveBeenCalledWith('/login')
Here’s the test in full:
//arrange
const url = `${appConfig.apiHost}/mockendpoint`
const redirectSpy = spyOn(globalErrorHandlerService, redirectFunctionName)
//act
httpClient.get(url).subscribe()
const mockRequest = httpTestingController.expectOne(url)
mockRequest.error(new ProgressEvent('error'), { status: 401 })
// assert
expect(redirectSpy).toHaveBeenCalledWith('/login')
Finally we add an afterEach
function to verify that there are no outstanding requests after each test.
afterEach(() => {
httpTestingController.verify()
})
I’ll leave as homework adding a test to make sure the redirect
method does not get called if the status is not 401
.
That’s it! I hope you found this useful.