Angular Testing Snippets: HTTP

Fake HTTP Responses from MockBackend

David
Sparkles Blog
3 min readMay 17, 2017

--

Angular comes with a thoroughly-designed HTTP Client that allows frontend apps to be cleanly decoupled from backends. A natural question is: can we run unit tests for our application with test doubles for an HTTP API? Because, if so, it’s possible to test how an Angular app reacts in response to fed-in data.

Here’s a step-by-step guide how to arrange unit tests with fake HTTP response data. We’ll also show how to assert request-response pairs at the HTTP boundaries. Everything is plain-old Angular testing: test are written in Jasmine; you can execute with Karma and webpack. Of course, you can also execute all of these tests right from of your favourite IDE, as Victor Savkin points out, giving you nice visual feedback and interactive debugging!

Step #1: Set Up

First, let’s set up the test. The central part is MockBackend, which is provided into the testing module as a replacement for XHRBackend(ll. 10–18). Note that a singleton instance is provided for both the MockBackend and the XHRBackend DI token.

With TestBed.get(MockBackend), we grab a reference for later use in our tests. In the tests, we will work directly with the API of MockBackend, whereas the application gets the mock as a stand-in replacement for XHRBackend. In result, the application is going to communicate with the mock.

A side note on mocks, fakes, and stubs: Know Your Test Doubles is a well-written explanation on the terminology. I will try to use it as precise as possible in this story!

Step #2: Dependency Injection in Tests

Second, let’s write a test specification that leverages our set up:

In the Angular testing framework,inject([Http, Mockbackend], () => {..)) injects services into the test. Remember that the injected MockBackend is the one instance that the application will talk to through the Http client.

The second parameter of inject()is an arrow-function (or: lambda) that takes the injected dependencies as arguments, provided in the same order as in the array:(http: Http, mockBackend: MockBackend) => {..}

Wrapping async(inject([], () => { .. })) sets up an asynchronous execution context for the test function. So, it waits for the async calls of the HTTP module to finish before completing the test.

Step #3: Faking HTTP traffic, expecting well-known values

With all the boilerplate code in place, let’s bring the test into life:

Let’s quickly talk through the HTTP flow that is happening here.

First, the Httpclient issues GET /foo/bar, which is served by MockBackend.

In mockBackend.connections.subscribe((c: MockConnection) => { .. }), the test responds to incoming requests and sends back a fake HTTP response to the client. Think of this as a mock implementation on the server-side.

Finally, we can expect() both request values (e.g. the request URL) and response values (e.g. response body) in the test. With a so-called test spy on Http.request we can track the number of requests made by the client. This looks obviously waste in such a simple example, but will be surprisingly helpful for more complex test cases.

Moving on…

In this testing pattern, we can make sure that the test client satisfies a web service’s API spec — or call it a contract, if you like.

A very nice thing is that we can now test from the Angular application right to a HTTP backend. The pattern will be even more powerful, when we use it with complex services. Think of an GitHubApiService that exposes an RPC-like API for exchanging domain objects, internally working on top of a RESTful API. The advantage of this pattern is to assert how the frontend reacts in certain situations to data received by the backend — we leverage HTTP as the cut-point for testing. Drawback: it stimulates to write more integration tests than true unit tests.

More ideas and testing recipes for Angular’s HTTP framework will be demonstrated in a later story! Stay tuned! if you enjoyed reading!

--

--