Angular Testing Snippets: Services over HTTP

Faking HTTP responses for domain model services

David
Sparkles Blog
3 min readMay 18, 2017

--

In single-page applications, business logic is often implemented over HTTP, translating application behaviour to URLs, request methods, status codes, and HTTP entities. This story presents a testing pattern for domain model services that implement RPC-like contracts between clients and servers. All is done by writing plain-old Angular tests: test specs in Jasmine, executed by Karma in a browser, test coverage from Istanbul, right from either WebStorm or VSCode.

Step #1: Implementing a super-duper feature

As an example, let’s take a simple login. It could be any other feature implementation of your choice:

Step #2: Test Set Up

Building upon the same idea discussed here, we are going to set up a mock backend for faking HTTP traffic. It serves two purposes: the MockBackend will be scripted to simulate a real backend service in our test specifications. Also, MockBackend will be injected as a drop-in replacement for XHRBackend, thus Http and dependees like FeatureService will issue requests that are served by the mock backend. Here’s the glueing code:

Step #3: Testing for Errors

Why are we writing all this boilerplate code? Well, we’d like battle-tested implementations that withstand even in faulty situations. So let’s go for testing a login failure first.

We’re pretending 401 Unauthorized is the response code for invalid credentials – the fake HTTP response is scripted in (3) in the following code listing. In (4), we assert that implementation of FeatureService responds to that by emitting a false value. Also, in (2a) and (2b), we’re asserting that the implementation issued a request that the server expected. The numbers in the code listing indicate the chronological order, helping us humans to trace the async flows.

If you like, you could split up the test into two separate specs. One that runs the expectations on the request, thus ensuring FeatureService satisfies the server-side API. A second one that runs the expectation on status: boolean, thus ensuring correct behaviour for users of FeatureService — typically, this would be components or other parts of the application.

This way, we’re running expectations at both ends: correct implementation against the API we’re depending on and correct behaviour towards users who depend on us.

Step #4: Testing for Success Cases

Rounding up our test specification, we should also test for the rare cases of success. The code listing should look familiar to you:

Summary

Let’s recall: what is the specific behaviour of the login feature?

First, the login method issues a POST /auth/login with form-encoded params for user and password. Second, response status codes from 200 to 299 are mapped to an Observable emitting true (obviously, that’s indicating login success). Any other response code and errors emit false to the caller (consequently, reporting login failures).

We’ve written two straight-forward test specifications ensuring expected behaviour of the client-side implementation in FeatureService for both success and error cases.

This kind of testing pattern can easily be scaled out to more complex services that run business logic over HTTP. It will help you to build battle-tested and bullet-proof implementations in your Angular apps!

--

--