Testing Angular 2 apps – Part 1: The beginning

Introduction

Angular 2 tends to be one of the hottest front-end framework last time. We decided to give it a try and now we are about to share thoughts on that topic. You can find some awesome articles over the web on how to make your first components or even much more advanced topics but we found it not enough to create big scalable application (like the LiveChat apps are).

Going back in the history, one of the reasons Angular 1 was introduced was a problem of testing front-end apps. It solved that problem quite nice and version 2 follows that path despite of being totally new piece of code.

What makes it good in it is separation of concerns. Unit testing (which will be considered here) is about ensuring that every single function of a system works perfectly OK itself without being even aware of any other part. If chosen framework (or library) doesn’t help with such a separation and doesn’t give ability of mocking dependencies, it is really hard to do it right over time (or maybe even impossible).

Angular 2 is easily testable – that’s true, but for now there is huge lack of resources on that topic (as we’re in beta.1 now!). That’s why we want to share our knowledge on that topic and start a discussion.

TypeScript or not?

As some of you remember, testing AngularJS was really about following rules set by Angular team and use their way of mocking, injecting etc. Similar things apply to the new version but not to such an extent. The point is Angular 2 intensively uses ECMAScript 2015/2016 features and all parts of ng2 app are now JS classes. What is the outcome? It can be now tested with pure JS (to some extent). Seems nice? Let’s start then.

Disclaimer: Code here is written in Angular 2 with TypeScript and Jasmine as testing framework. Although first test can be done in pure JavaScript, the final ones will use TypeScript to integrate with Angular 2 much closer. It means it’s better to configure test environment first and then gradually come to more complex test cases. If you haven’t got one yet you can take the one I’m working on here: https://github.com/wkwiatek/angular2-webpack2.

First tests

Note: Article is based on Angular 2 beta.2

Gist with examples for whole series (checked for beta.16) is here: Angular 2 testing gist

The beginning is first component – the App. Angular 2 apps are all made of components so the whole app is also a component. It may look like this:

import {Component} from 'angular2/core';

@Component({
  selector: 'app',
  template: '<span>Hello</span>'
})
export class App {}

It’s basically a class with decorator which adds some logic used by Angular. The important thing is that decorators add some stuff to the class. It means we can still do: new App() and come with pure JS object.

Let’s add a property:

@Component({
  selector: 'app',
  template: '<span>{{hello}}</span>'
})
export class App {
  private hello: string = 'Hello';
}

And now let’s move to tests. My personal preference (and the one that we use in LiveChat) is to keep tests as close to the code as possible. When we have a file like app.ts which contains the code from snippet above then we would create a file named app.spec.ts and put the following stuff in there:

import {App} from './app';

describe('App', () => {

  beforeEach(function() {
    this.app = new App();
  });

  it('should have hello property', function() {
    expect(this.app.hello).toBe('Hello');
  });

});

Note: It’s good to write failing tests first and then make it passing – even when no TDD is introduced. It’s just about making sure that particular test is running and is not a false positive. In Jasmine you can see very often statement like expect(...).not.toBe(...).

Simple? I presume yes. And an aha moment is: we are now able to test Services, Pipes, basic Components and everything what is a JS class!

Want to test some method in component? Here it is:

@Component({
  selector: 'app',
  template: '<span>{{sayHello()}}</span>'
})
export class App {
  private name: string = 'John';

  sayHello(): string {
    return `Hello ${this.name}`;
  }
}
import {App} from './app';

describe('App', () => {

  beforeEach(function() {
    this.app = new App();
  });

  it('should have name property', function() {
    expect(this.app.name).toBe('John');
  });

  it('should say hello with name property', function() {
    expect(this.app.sayHello()).toBe('Hello John');
  });

});

Note: We use good old anonymous function() in there. It’s because we use the difference between it and an arrow function – context. Every function gets its context from the code that it was invoked from. So this in the jasmine test is the same across particular beforeEach, it and afterEach combination.

Too easy for you? Stay tuned for the next part when I’m going to cover dependency injection and component tests with DOM.

Part 2 is here

Tagged: , , ,

Categorised in: ,

  • TheBigKosMos

    TypeError: Cannot set property ‘app’ of undefined (with ng 2.0.0-rc.1)

    using:

    var app;

    beforeEach(function() {
    app = new App();
    });

    as a workaround

  • lricoy

    Thanks for sharing! It’s worth noting that another good way to start a new project is using the Angular CLI https://github.com/angular/angular-cli#generating-components-directives-pipes-and-services

  • Erik Bors

    Hi, thanx you havent written information how to run the tests … im little bit confused. Thanx.

    • Wojciech Kwiatek

      > It means it’s better to configure test environment first and then gradually come to more complex test cases. If you haven’t got one yet you can take the one I’m working on here: https://github.com/wkwiatek/angular2-webpack2.

  • Thank you for your time for writing these articles!