Testing Your Angular 2 Application: Part 2 of 2

This post was written when Angular was in beta. The concepts in this post are all still applicable today but the testing tooling has changd significantly. Updated documentation can be found at https://angular.io/guide/testing

In part 1 of this series
we defined a QuoteComponent that displays a random quote on a web page. We showed how to use Angular 2's testing utilities to mock out QuoteService and test the QuoteComponent's presentation logic in isolation.

In this post, I'll do the opposite: I'll isolate the QuoteService and show how to unit test the code at your application's service level. By the end of this post, you should have all the tools you need to test a non-trivial Angular 2 web application.

Since QUOTE_DATA is just a list of hard-coded records, we can replace it with some data that's tailored to the behaviour we want to test. This is done using provide's useValue option: whenever Angular 2's dependency injector is asked for QUOTE_DATA, it will use our test data instead.

The RandomNumberService, on the other hand, can be stubbed out with an empty class that we'll manipulate later. Think of this as partial typings for the bits of RandomNumberService that we care about in this test.

Unlike a mock, a stub does not result in in complex mocking logic being shared across different test suites; we'll leave it up to each specific test to supply the relevant implementation of this simple service.

Whether to use a stub or a mock is largely a matter of taste; I tend to prefer stubs where possible because they decouple tests from each other and are in many cases easier to reason about.

Set up an Actual Test

Now that we've set up our test environment, we can write an actual test.

This is a good example of stub-based unit testing: I'm beginning with an
empty stub for RandomNumberService and swapping in the minimum implementation for a meaningful test. This way I don't have to maintain a complex mock object in addition to the real one.

I verify behaviour using Jasmine's createSpy, which creates an instrumented, fake implementation of the pick method.

I personally find that this is a very clean, readable, and flexible testing style.

This implementation is straightforward: it uses Angular 2's http service to hit QuotesOnDesign's JSON endpoint, and then converts the response data to the IQuote interface used by our QuoteComponent.

However, this example has two aspects that complicate testing:

It returns data asynchronously using an Observable.

It relies on Angular 2's HTTP infrastructure, which is difficult to isolate.

Fortunately, Angular 2 gives us the tools to handle these cases as well.

Handling Asynchronicity

We'll import a new helper from angular2/testing called fakeAsync. This is a simple wrapper function that places your test in the "async zone": this causes asynchronous code to run in a synchronous way via the magic of zone.js.

Mocking out the HTTP Backend

The new implementation of QuoteService relies on the http service provided by Angular 2. However, because this is a unit test, we don't want to actually make HTTP calls - we need to be able to control the environment in which our tests run.

We could use a stub implementation of HTTP as we did above; and in some cases that's appropriate. However HTTP is a fairly complex interface. In this case, it's easier to use some mocks that Angular 2 has already made available to us:

Wrap-up

Angular 2 provides a great set of tools for testing everything from your UI
components down to your HTTP services. Between parts one and two of this series, you should be equipped to handle most scenarios you may encounter when unit testing your Angular 2 apps.