The new component based architecture in Angular is great for unit testing, so in the following article I will demonstrate how to unit test Angular 2.0 code using Jasmine and TypeScript.
A lot has changed in Angular, but as with most things, the underlying concepts are not that different at the end of the day. This is also true for unit testing as you'll see after going through my samples.
I have tried to identify a few common scenarios, and will through the following examples recommend unit testing patterns for these scenarios.
By far the simplest case is writing unit tests for a component without dependencies. These components are perhaps rare, but it's a good starting point. For the purposes of this article I've created a simple DisplayName component. As you can tell the component displays a person's full name by combining first name and last name.
The logic here is trivial, but I still think it deserves a unit test.
The first thing to notice is that the entire test fixture is wrapped in a method called main. This is just a simple wrapper, but it seems to be the convention followed by the Angular team.
The sample above shows two variations on the test. The first one is the most straight forward since it simply instantiates the component class manually before calling the generateFullName method. Finally I have defined an expect on the fullName property.
In the second version of the test I have made use of DI to instantiate the component class. This requires a little bit more setup code since we have to tell Angular how to inject the component. As you can tell I am using the beforeEachProviders function in combination with inject to wire this up. You can basically think of this step as bootstrapping the code under test. Similar to how you would bootstrap your full application code.
Service with Dependencies
It might be overkill to use DI in the previous example, but when we introduce dependencies, you will hopefully see the benefits of DI in unit tests.
In the next example I have defined a CustomerService with a ProductService dependency.
In this case we can't instantiate the CustomerService without the ProductService, so it's easier to use DI when instantiating rather than doing it “long hand”.
In the test I am registering the dependencies, so that the final injection of CustomerService will cascade down to include the dependency on ProductService.
The previous example showed how to use DI in unit tests, but there is one problem with the implementation. When testing CustomerService we are directly hitting ProductService, which technically turns this into an integration test. Strictly speaking a “pure” unit test should focus on a unit in isolation and simulate the integration points. In the current implementation that might not be a big deal since the return values are hard coded. However in a more realistic implementation it's likely that ProductService would rely on external dependencies like a database or external api.
In the following example I will show how to override the ProductService dependency and replace it with a mock.
The implementation is very similar, but notice how I am intercepting the provider registration by defining a mocked version called ProductServiceMock.
I generally don't include markup testing in my unit tests, but Angular 2.0 makes this relatively easy through the TestComponentBuilder api. I have included a sample to demonstrate how to check for DOM changes based on events in the component. Specifically I will simulate typing into a textbox and bind the textbox value to a span tag.
The Angular test api makes it very easy to instantiate the component and run assertions agains the rendered view. The key concept here is the TestComponentBuilder that lets me create an instance of the component with access to both the component class and DOM.
Many common applications of observables (e.g. http) assume asynchronous execution, but observables are actually synchronous by default. As you may have guessed we can take advantage of this when mocking observables. If we replace asynchronous observables with synchronous mocks we can take the asynchronous part out of the equation when testing subscription handlers.
In the code sample below I want to test that the correct action is dispatched after the asynchronous getLogEntries method completes.
getLogEntries() returns an observable, but by mocking the asynchronous observable using a synchronous observable, we no longer have to worry about async execution in our test.
The key to this is using Observable.of(..) to set up an observable that completes synchronously.
Hopefully this will get you started on unit testing in Angular. I will expand on this article over time to include even more patterns and scenarios.
As always my code is available on Github.