WTF is Angular TestBed

Angular ships with a unit testing API called TestBed, which is specifically designed for writing unit tests for the Angular framework. It allows for simple testing of Angular web components, that is to say a TypeScript class, paired with some HTML.

For a brief intro to unit testing concepts see here

If you use the Angular CLI for generating your app and your components (which you should), then it will by default set up a <component -name>.component.spec.ts file, with some boilerplate setup code for TestBed testing already populated.

Let’s have a look at how we might write a simple login component that is properly tested, using Test Driven Development, the Angular CLI and TestBed.

Login Component

Before writing any code, we should think about what the component is supposed to do.

This step is important, because if the behaviour of the component is not properly defined, we cannot write tests for it, and we can’t properly define the interface. This behaviour may need to change later on, but for now we need a design contract to code against. For a  basic login component, I’m going to say I want:

  • login form
  • input box of type text for username
  • input box of type password for password
  • button which emits a login event with a payload of username and password

This is enough for me to write a suite of tests, and to develop my component against those criteria.

I’m going to set up a quick angular project using the Angular CLI in order to demonstrate all of the default test tooling that comes with Angular:

I will create one called ‘test-app’

ng new test-app

Once this command has run you will have a default hello world application. To view it, move into the directory of the new application and run

ng serve

If you open your browser and type localhost:4200 into the url box you should now see the default Angular application.

For now we will just use the app.component component as a sandbox for testing our new login component. For the first step, delete all the default code from app.component.html, as this is where we will be putting our new component eventually.

Also delete app.component.spec.ts as these tests will now fail because we have changed the underlying component.

Now we are ready to create our login component:

ng generate component login

This will create a new Login component, with a selector called app-login adding it as a declaration in app.module.ts. These files will be created:

login.component.html login.component.css login.component.ts login.component.spec.ts

This component can now be used in the app.component.html template file, so let’s add it now. app.component.html should now have only the following code in it:

<app-login></app-login>

The app at localhost:4200 in your browser should now say something similar to below:

login works!

TestBed setup

Before we start writing tests, let’s take a look at the generated login.component.spec.ts file to get a better understanding of how TestBed is set up:

    
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

    import { LoginComponent } from './login.component';

    describe('LoginComponent', () => {
      let component: LoginComponent;
      let fixture: ComponentFixture<LoginComponent>;

      beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [ LoginComponent ]
        })
        .compileComponents();
      }));

      beforeEach(() => {
        fixture = TestBed.createComponent(LoginComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });

      it('should create', () => {
        expect(component).toBeTruthy();
      });
    });

This sets up the TestBed environment, and one basic test ‘it should create’. Let’s run the tests now and see what happens:

ng test

A chrome window should open, showing the test output. If everything is working, it will look something like below:

So now we have a test than ensures our component can be instantiated correctly. At this point it is worth quickly digressing and explaining what TestBed.configureTestingModule does.

It basically allows you to set up a miniature Angular application, with only the components, providers, modules etc. that are needed for the specific piece of code you are testing. In our example it currently just has a declarations array, with our LoginComponent in it.

Specifically, the block below sets up the testing module, then compiles all the components. Again, in our case this is just the LoginComponent.

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginComponent ]
    })
    .compileComponents();
  }));

The compileComponents call is aysnchronous, so to make the tests easier to understand and avoid dealing with promises explicitly, we use the Angular testing module’s async call to ensure that all of the asynchronous code has finished before the next block of code is run. Essentially this means that the asynchronous code can be read synchronously.

Once this is done we get references to the ComponentFixture of type LoginComponent and the LoginComponent itself:

  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

When we start writing tests we will see how these two variables can be used to interact with our component and to assert that it is behaving correctly.

Jasmine provides the syntax for writing our tests in a behaviour driven style. A detailed analysis of Jasmine is beyond the scope of this post. For more information read the docs. For now all you need to know is that Jasmine is what we are using when we use beforeEach, describe, it and expect etc. functions.

Time to add some tests!

Now we can begin adding tests to cover our design contract, I.E. the things that our component will do, when interacted with in specific ways.

I normally just set up a bunch of empty it blocks as below with the things I’m expecting the component to do. Notice that this reflects our original design contract:

it('should have a form named loginForm', () => {

});

it('should have an input box of type text with the name username', () => {

});

it('should have an input box of type password with the name password', () => {

});

it('should have a button with the text login that emits an event with a payload with username and password, based on form inputs', () => {

});

At this stage we are just looking to organise our thoughts. Once we are relatively happy that we know what we want to test, we can start adding the code to actually test the component. These new tests will fail, as we haven’t written any code yet. Then we can write code to make them pass and in theory our component will work.

Let’s start with the first test ‘should have a form name loginForm’:

it('should have a form named loginForm', () => {
  expect(fixture.debugElement.query(By.css('form[name=loginForm]'))).toBeTruthy();
});

Here, the fixture variable allows us to query our component and check it’s DOM for whether certain elements are present. In this case we are checking that a form with the attribute name=loginForm is present on the component. Obviously currently this is not true, so if your tests are still running, they should fail now:

For a more detailed look at how to query your components for specific DOM elements etc. it is best to look at the Angular documentation on testing

OK so now we have a failing test, let’s fix it. Adding the following code to your login.component.html ought to do it:

<form name="loginForm"></form>

Now let’s fill in the rest of login.component.spec.ts:

it('should create', () => {
  expect(component).toBeTruthy();
});

it('should have a form named loginForm', () => {
  expect(fixture.debugElement.query(By.css('form[name=loginForm]'))).toBeTruthy();
});

it('should have an input box of type text with the name username', () => {
  expect(fixture.debugElement.query(By.css('input[type="text"][name="username"'))).toBeTruthy();
});

it('should have an input box of type password with the name password', () => {
  expect(fixture.debugElement.query(By.css('input[type="password"][name="password"'))).toBeTruthy();
});

describe('loginButton', () => {

  it('should have a button with id loginButton and text \"Login\"', () => {
    const loginButton = fixture.debugElement.query(By.css('button[id=loginButton]'));
    expect(loginButton).toBeTruthy();
    expect(loginButton.nativeElement.innerText).toEqual('Login');
  });
});

If you run your tests again now you should have some failing specs. To fix them, add the following code to your login.component.ts file:

<form name="loginForm">
  <input type="text" name="username">
  <input type="password" name="password">
  <button id="loginButton">Login</button>
</form>

run the tests again:

So now we have a tested component, designed to a contract, which has a spec file documenting what HTML elements are present on it. Pretty cool! Now if anyone accidentally breaks that contract, a big red message will tell them off 🙂

Notice that in the Karma chrome test output, our component is being rendered to the screen. This is important to understand. We are actually testing our component with a browser to ensure that it is rendered and behaves correctly. This is very powerful!

You might have noticed that our test specification has changed slightly from the first round of tests. This is fine and in fact a good thing. As you start to develop you may find that your assumptions were wrong, and you should adjust your tests and assumptions as you go to reflect any new information or changed design decisions.

Clearly at the moment our component is a bit limited in functionality. Let’s add a test to check that when the button is clicked, it emits an event with username and password based on the form field values:

describe('loginButton', () => {

  let loginButton: DebugElement;

  beforeEach(() => {
    loginButton = fixture.debugElement.query(By.css('button[id=loginButton][type="submit"]'));
  });

  it('should have a button of type submit with id loginButton and text "Login"', () => {
    expect(loginButton).toBeTruthy();
    expect(loginButton.nativeElement.innerText).toEqual('Login');
  });

  it('should have a button that emits an event with a payload of username and password, based on form inputs', (done) => {

    // set up test data for use in our test
    const testUserDetails = {
      username: 'user01',
      password: 'superSweetPassword01!'
    };

    // subscribe to the emitted login event and ensure that it fires and has correct data
    component.login.subscribe((data) => {
      expect(data).toEqual(testUserDetails);
      done();
    });

    // find the input fields and the login button in the DOM and interact with them
    const usernameInput = fixture.debugElement.query(By.css('input[name=username]')).nativeElement;
    const passwordInput = fixture.debugElement.query(By.css('input[name=password]')).nativeElement;

    usernameInput.value = testUserDetails.username;
    usernameInput.dispatchEvent(new Event('input'));
    passwordInput.value = testUserDetails.password;
    passwordInput.dispatchEvent(new Event('input'));

    loginButton.nativeElement.click();

    // trigger change detection so our code actually updates
    fixture.detectChanges();

  });
});

I will be honest, this code is a bit more complex than what we have seen so far… let’s break it down:

We need to test that our component emits an event called onLogin which has the correct data when the login button is clicked. This is something we would probably achieve in Angular by using an EventEmitter, which emits a stream from the component that can be subscribed to. As it is a stream it is asynchronous, so here we make use of Jasmine’s method for dealing with testing asynchronous code: namely the done method. By default, if the done() call is not carried out within 5000 ms then the test will fail.

it('should have a button that emits an event with a payload of username and password, based on form inputs', (done) => {

    const testUserDetails = {
      username: 'user01',
      password: 'superSweetPassword01!'
    };

    component.login.subscribe((data) => {
      expect(data).toEqual(testUserDetails);
      done();
    });

The remainder of the spec file is code dedicated to interacting with the DOM elements by sending click events etc. After we have made these changes we have to call fixture.detectChanges() in order to trigger Angular’s change detection, and actually update our test environment. Essentially what we are testing here is that after populating the form inputs via the DOM, and clicking the login button, our component emits an event. Crucially, we want to ensure where at all possible we are interacting with the DOM directly in our tests, as this is the closest simulation to how a user might interact with our component.

Again, our tests will fail now, as we haven’t written an event emitter yet, or tied it to the form inputs. To fix this we add the following code to login.component.ts so it now looks like below:

import {Component, EventEmitter, OnInit, Output} from '@angular/core';

export interface LoginDetails {
  username?: string;
  password?: string;
}

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  @Output() login = new EventEmitter();

  userDetails: LoginDetails = {};

  onLogin() {
    this.login.emit(this.userDetails);
  }

  constructor() { }

  ngOnInit() {
  }

}

And these changes to our login.component.html:

<form #loginForm name="loginForm" (ngSubmit)="onLogin()">
  <input type="text" name="username" [(ngModel)]="userDetails.username">
  <input type="password" name="password" [(ngModel)]="userDetails.password">
  <button type="submit" id="loginButton">Login</button>
</form>

Notice that we are now using ngSubmit and ngModel. These require the FormsModule to be imported into the app.module.ts:

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Because TestBed is just a way of setting up a mini app, we will also have to add this FormsModule import to ourlogin.component.spec.ts:

beforeEach(async(() => {
  TestBed.configureTestingModule({
    imports: [FormsModule],
    declarations: [LoginComponent]
  })
    .compileComponents();
}));

In order to properly test NgModel based code via native inputs, we also have to add the following block to our spec file, because NgModel is asynchronously updated :

beforeEach(async(() => {
  fixture.detectChanges();
  fixture.whenStable();
}));

With those changes, the tests should now pass:

NB While I have written the tests first for this post, quite often you will end up writing bits of functionality first and then testing them. As with most things TDD is a great tool when not used dogmatically. If you are messing around with different implementation options, it can make sense to hack about a bit first, then to add the tests. Basically don’t let the perfect be the enemy of the good 🙂 Try writing the tests first and then implementing the feature, but don’t be too rigid if you find you need to try some other stuff first. The important thing is that eventually your code is properly covered by meaningful unit tests which clarify how it is to be used, what is does, and protects it from regression bugs.

For the full source code look here

WTF is unit testing?

What are unit tests?

Unit tests are designed to test the smallest sensible ‘units’ of code in a software system in isolation. This isolation is achieved by using mock versions of any dependencies, which can be ‘spied’ on to ensure they are called correctly. This is one of the reasons why dependency injection is so good, as it allows you to pass in pretend versions of other code.

Essentially, unit tests allow a developer to ensure that their unit of code behaves in a predictable way, including any interactions with dependencies, given specific inputs.

They are most effective when they are fast to run, and can be run automatically during development or before a build.

They are not integration tests (https://stackoverflow.com/questions/5357601/whats-the-difference-between-unit-tests-and-integration-tests)

Well written unit tests also have the benefit of providing documentation for the interface the unit of code exposes to the rest of the system.

This can be very powerful, as any developer who has to work on something you have tested can refer to the tests an immediately get a clear definition of what the unit of code does, and how they should use it.

Why write unit tests?

Writing unit tests the right way will make your code better!

Writing unit tests for the sake of it, or to achieve coverage numbers may make your code better, but won’t necessarily. It is important to understand why unit testing is useful and what benefits it can offer before you get started.

Testing is a tool, just like anything else. It will not magically fix everything if it is not done properly.

Here are some of the potential benefits of maintaining a comprehensive set of unit tests:

  • Helps to document your code for other developers and yourself.
  • Flushes out bugs.
  • Improves design decisions.
  • Acts as buffer against low level regression bugs.

Read these for more information on the benefits/reasons for unit testing:

How to unit test:

The pattern for writing tests will basically be the same regardless of what the unit of code being tested is:

1) Create an instance of the unit of code that you wish to test. 2) Mock and spy on any dependencies that your unit of code interacts with. 3) Interact with the unit under test via its public interface. 4) Ensure that the unit under test behaves as expected after it is interacted with, including calling any mocked dependent code correctly.

If for any reason any of these steps is difficult to do, then you may have to rethink how you have written your code. This is why writing tests can help with design decisions. As a general rule if something is difficult to test, it may not be sufficiently modular and/or it may be too tightly coupled to other areas of the system.

For example, if you directly import a dependency inside a class, rather than passing it in via dependency injection, you will be forced to directly call the dependency. This may be what you want, but it may not be. Unit testing will force you to think about things like this.

WTF is an API?

It took me an embarrassingly long time to really figure out what an API is.

This is what Wikipedia says:

In computer programming, an application programming interface (API) is a set of subroutine definitions, protocols, and tools for building application software. In general terms, it is a set of clearly defined methods of communication between various software components. A good API makes it easier to develop a computer program by providing all the building blocks, which are then put together by the programmer. An API may be for a web-based system, operating system, database system, computer hardware or software library. An API specification can take many forms, but often includes specifications for routines, data structures, object classes, variables or remote calls. POSIX, Microsoft Windows API, the C++ Standard Template Library and Java APIs are examples of different forms of APIs. Documentation for the API is usually provided to facilitate usage.

Clear? No? I wasn’t either…

Here is my non Wikipedia version:

API stands for Application Programming Interface. The word Interface is used to describe the point at which two systems communicate with one another across a boundary. In order to do this communication they require a defined language, this is what an API is. It is basically any language used to interact with a computer system.

The alternative would be writing code in zeros and ones, as that is fundamentally what all these clever computer things are doing under the hood. Anything else is an abstraction. Obviously that would be somewhat time consuming however.

One of the things that was confusing to me is that programming languages themselves are APIs. That is they are a set of methods that can be used to interact with a computer system. If you think of your TV as a system, then the API would be the TV remote. It has buttons, corresponding to things that you want the TV to do, such as ‘display guide’, ‘mute sound’ etc. After you have pushed a button, the TV will carry out a series of actions to reflect your instruction. How it does it is not available to you, or important to you. You only care that it does it according to the contract defined by the TV remote (I.E that pressing mute will turn off the sound on the TV).

Another thing that was VERY confusing to me is that in the wider world, when APIs are talked about, it is almost always in reference to an API, or set of methods, or language, for interacting with a web service. So for example, people will talk about ‘Google’s API’, or ‘British Airways’ API’. In these cases, they are referring to the defined language that is used to interact with these services, normally via HTTP. However, the principle remains the same. An API is a strictly defined language for interacting with another system. Aside from those constraints, it can take any form.