WTF is a splash sreen? (Angular edition)

A splash screen is something which renders before the rest of an application is ready, and blocks the rest of the UI.

It avoids a user having to sit staring at a semi-functional product, and having the UI judder as more content is loaded.

OK I know what a splash screen is, how do I do one in Angular!?!

Add an element that you want to be your ‘splash’ to your index.html. Below we add a div and then an image inside it

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>World's angriest cats</title>
    <base href="/" />

    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/x-icon" href="favicon.ico" />
    <link rel="stylesheet" type="text/css" href="assets/styles/splash.css" />
  </head>
  <body class="mat-typography">
    <app-root></app-root>
    <div class="splash">
      <img
        class="splash__image"
        src="my-angry-cat.jpg"
        alt="Angry cat splash page"
      />
    </div>
  </body>
</html>

And add some CSS similar to this to make sure it fills the whole screen and bleeds in/out etc.

.splash {
  opacity: 1;
  visibility: visible;
  transition: opacity 1.4s ease-in-out, visibility 1.4s;
  position: fixed;
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
  background: red;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
// This little piece of trickery hides the splash image once the app-root is populated (i.e. the app is ready)
app-root:not(:empty) + .splash {
  opacity: 0;
  visibility: hidden;
}

.splash__image {
  width: 100%;
  max-width: 200px;
}

OK cool, but what if I want to wait for a specific API call to finish (e.g. CMS) before rendering my site?

No worries. Angular has you covered here too 🙂

Assuming that you have a provider called ContentProvider, which is doing some asynchronous task to get content for your site, and then exposes a stream to tell you when the data is ready, you can tell Angular that it needs to wait until this is done using the slightly confusingly named APP_INITIALIZER injection token.

This, as far as my puny brain can approximate, adds the function returned from your useFactory method to an array of methods which are called before the app initialises and starts rendering.

If the function you return returns a promise, as below, then the app will not initialise until that promise resolves.

    {
      provide: APP_INITIALIZER,
      useFactory: (content: ContentProvider) => {
        return async () => {
          return new Promise(resolve => {
            content.loadContent();
            content.loaded.subscribe(loaded => {
              if (loaded) {
                resolve();
              }
            });
          });
        };
      },
      multi: true,
      deps: [ContentProvider],
    },

WTF are feature flags? (Angular example included)

Feature flags are a fairly rudimentary (but I think powerful) alternative to long lived feature branches, and the associated merge hell that goes with them.

You basically merge unfinished or experimental features to your master branch, and even deploy them, but hide the functionality behind ‘flags’, only allowing them to be viewed if certain switches are activated. The exact mechanism behind the switches doesn’t really matter, and I will detail some potential ways of implementing these switches in the context of an Angular application below.

Feature flags do not mean merging broken code or sloppy code.

The code should still be tested and peer reviewed, and your automated suite of unit, integration and e2e tests, linting and other build scripts that you have written to protect your master branch should still run (you do have those things… right?).

The feature is merely unfinished.

Perhaps it is missing some UI polish, or requires further QA before being released into the wild proper, or perhaps you want to test it out on a smaller subset of your user base to gather analytics feedback on the effectiveness of the feature.

There are many reasons why feature flags can make sense, but fundamentally they are a very simple concept.

They just give you the ability to ‘switch’ on or off different parts of your application, either at run-time or build time.

For me, the main benefit is that your feature is regularly integrated back into master, without having to wait for all of the functionality to be complete. This ensures that at all times you can be sure that your feature plays nicely with the rest of the code base, and with any other features that other people are working on (also hidden behind feature flags).

Also they are a great tool if you want to demo experimental features to stakeholders early on in the development process, in order to gather feedback.

I’ve managed to use them successfully before on a site serving thousands of daily visitors, without causing any disruption to the deployed product, and I was able to demo the feature in the live site.

Where to store feature state?

To use feature flags, you need some state somewhere that your app is aware of, that tells it whether certain features are enabled or not.

For example, say we have an experimental kitten generator feature that we only want to show sometimes, we might have a piece of data somewhere called kittenGeneratorEnabled, which our app checks to see if it should allow a user access to the feature.

There are many places this data could live, and your particular use case will dictate the most sensible course of action:

  • Build configuration files (in Angular’s case we would probably use environment files for this). We can set the switch to on/off at build time and our deployed app will reflect this. The downside to this approach is that it not super flexible, as you can’t switch the feature on/off at run-time. If you have a slick and speedy build pipeline, you can still turn off features in subsequent builds quickly and easily if there are any problems with them. It is also a pretty light weight solution, it just costs you a tiny bit of configuration. You can also have builds for different contexts, for example you could disable all experimental features in production, and turn them on for QA or something similar (not saying this is a good idea, just an example of the level of control this can give you).

  • A server. We can delegate the responsibility for setting feature flags to a backend service. If you have more complex requirements, for example only showing features to a specific subset of users via traffic splitting etc. this might be worth looking into as the complex logic/management portal for this sort of granular control can be moved out of the frontend. In our kitten generator case we would query an endpoint like GET /kittenGeneratorEnabled to figure out whether to show the user the feature. If the features are potentially insecure and you want to make sure they are only viewed by people you trust, rather than nasty malicious types, then you could make users authenticate and control access to the features via the backend too. The main downside of this is that it is pretty heavy weight, and requires more communication between any frontend and backend developers.

  • Browser storage. Cookies, local storage, session storage etc. can be used to keep the state and can then very easily be queried in your frontend code to see whether to allow access to features or not. In the case of the server example above, you could have the endpoint set and unset cookies, then have your app read these cookies. These browser storage items can also be set and unset via the browser address bar with query parameters. This has the advantage that you can send people links with certain flags enabled/disabled for demos/testing, rather than having them manually mess around with browser storage. These links will also work equally well on mobile devices, where setting custom cookies etc. is much harder.

Obviously you can, and probably should use a combination of the above approaches to meet your needs.

How to control access to features

Most features in a modern web application will be routed. In our case there would probably be a /kitten-generator route in the frontend.

If this is the case then the simplest way to control access to the feature in Angular is to use a conditional route guard.

Other features/functionality can be controlled with simple if/else statements (as in if(featureEnabled){ do a thing }).

Basically this stuff shouldn’t be complex in a well architected application. If your application consists of one app.component.ts with thousands of lines of code in it, then you will have a more difficult time. In that case feature flags are probably the least of your problems!

Generally though, you will be able to switch a feature off by simply blocking the route with a route guard.

What would a route guard for something like that look like?

via GIPHY

Let’s say we have decided to use the browser’s local storage to set individual feature flag state (in our case the kitten-generator-enabled flag).

We have also decided to add an additional safety value in our Angular environment file called featureFlags which when set to true will allow access to any experimental features if the correct local storage value is set, and when set to false, will not allow access to any experimental features, even if the user has the correct local storage value set. This means that if we are being super paranoid we can have builds for production which do not expose any experimental or half finished features.

import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  CanActivate,
  Router,
  RouterStateSnapshot,
} from '@angular/router';
import { environment } from '../../environments/environment';

@Injectable({
  providedIn: 'root',
})
export class FeatureRouteGuardService implements CanActivate {
  constructor(public router: Router) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot,
  ): boolean {
    if (!environment.featureFlags) {
      this.router.navigateByUrl(route.data.featureFlagDisabledRoute || '/');
      return false;
    }
    if (localStorage.getItem(route.data.featureFlag)) {
      return true;
    }
    this.router.navigateByUrl(route.data.featureFlagDisabledRoute || '/');
    return false;
  }
}

And we would use it like this:

    RouterModule.forRoot([
      {
        path: 'kitten-generator',
        component: KittenGeneratorComponent,
        canActivate: [FeatureRouteGuardService],
        data: { featureFlag: 'kitten-generator-enabled', featureFlagDisabledRoute: '/' },
      }

Now, if a user has a kitten-generator-enabled:true value in their browser’s local storage, and they are accessing a build with the featureFlags value set to true in the environment file, then they will be able to access this route, otherwise, they will be redirected to /.

Going one step further, if we wanted to implement the logic to set or unset features via the browser url, we might use something like this inside the app’s app.component.ts

  /**
   * parse the navigation bar query parameters, and use them to set
   * local storage values to drive feature flag activation
   * @param queryParams
   */
  private processFeatureFlagQueryParams(queryParams: Params) {
    const featuresToTurnOn =
      (queryParams['features-on'] && queryParams['features-on'].split(',')) ||
      [];
    const featuresToTurnOff =
      (queryParams['features-off'] && queryParams['features-off'].split(',')) ||
      [];
    featuresToTurnOn.forEach(item => {
      localStorage.setItem(item, 'true');
    });
    featuresToTurnOff.forEach(item => {
      if (localStorage.getItem(item)) {
        localStorage.removeItem(item);
      }
    });
  }

  ngOnInit() {
    if (this.environment.featureFlags) {
      this.router.events.subscribe(event => {
        if (event instanceof ActivationStart) {
          this.processFeatureFlagQueryParams(event.snapshot.queryParams);
        }
      });
    }
  }

This then would mean that you could give someone a link to your site like this /kitten-generator?features-on=kitten-generator,another-experimental-feature&features-off=yet-another-experimental-feature.

This would set local storage values to true for kitten-generator and another-experimental-feature, and would delete any existing local storage value for yet-another-experimental-feature

Conclusion

Feature flags in web apps and Angular can be as simple, or as complex as you’d like them to be.

I generally have had a better time with the slightly simpler ones shown above, than all singing all dancing server config options. What are your thoughts?

HTF do I write E2E tests with a stubbed dependency? (Angular/Nrwl Nx edition)

In an earlier post, I went over some of the reasons you might want your E2E tests to run against a stubbed version of any really slow or unpredictable dependencies.

Let’s see how this might look in an Angular context, making use of the framework’s inbuilt dependency injection, and TypeScript.

NB I’m going to use Nrwl Nx to spin up these example bits of code inside a shared ‘workspace’ or ‘monorepo’ (the terminology seems to change).

If you are unfamiliar with Nrwl Nx you should check it out! I’ve used their tooling extensively at work, and after a few hiccups can strongly recommend it. See here for a slightly outdated explanation of why/how I’ve used their stuff in the past.

Basically though, they provide a way of easily building multiple applications/libraries from within one repository, supporting code reuse between applications, with convenience tooling, built on top of the Angular CLI, for handling unit testing, e2e testing, builds etc. as well as a bunch of generators for generating opinionated bits of ‘best practice’ Angular/ngrx code. (And you get all of this for freeeee!)

The method I’m using for stubbing dependencies would be equally applicable to a ‘vanilla’ Angular application however.

Meet the players

1) Our main Angular application. It has a button which when clicked will call our Slow Ass Api’s getInformation endpoint, and when finished will render the result (of type SlowInformation).

2) Our Slow Ass Api. This is a separate TypeScript library that exposes one method, getInformation, which returns an object of type SlowInformation. The (not so) clever bit is that this call will simulate unpredictable slowness, by returning the data after a random amount of time from 0ms up to 10000ms.

3) Our E2E tests. I am going to use Cypress for these because I happen to really like it. If you want to use Protractor, this method will still work, you will just have to use the executeScript method to talk to the window object, instead. Also, if you create your application with Nrwl Nx they will set up all of the code scaffolding and configuration for you to support either Protractor or Cypress (you can choose as part of their interactive setup script).

The action

Below are a bunch of links to specific commits. If you want to play along you can clone the project and checkout individual commits to see how the tests behave at various stages of development.

I will pick up from after the initial project skeleton is put together. As I mentioned, this was done using Nrwl Nx’s extensions to the Angular CLI. If you are curious, you can see what these tools do for you in this commit

Generate slow-information-renderer Angular app (commit):

This uses another of the Nx scripts to generate a fully fledged Angular app, I chose to use Jest and Cypress, and so it also sets up an e2e testing project, which can be run with npm run e2e

Generate slow information renderer component and slow information service (commit)

These will be responsible for asking the user to click a button, which will ultimately call our service to call the slow ass api.

Again, this makes use of generator commands to create a boilerplate component with best practices.

Generate and write slow ass api library (commit)

Due to an over eager interactive rebase I have accidentally lumped my changes in with the generator changes.

Basically though, here I create a library for returning strongly typed data of type SlowInformation after a random delay (I even unit tested it! I got carried away…)

The main file to concentrate on is slow-ass-api.ts

Plug the library into our Angular Application, and do some wiring (commit)

After this, our app will render data from the slow-ass-api after a delay

Add a naive e2e test (commit)

This commit adds a basic e2e test which is, unfortunately, quite flaky. Because of the random delay in the data coming back, the test sometimes passes, but sometimes doesn’t. This can be demonstrated by making the API resolve more quickly (commit)

With this change, our tests behave properly, unfortunately in the real world you can’t just tell a slow api to be quicker. So we will need a different solution.

Make the test work less flakily (commit)

Here, we experiment with making our test wait 10 seconds to make sure all the data is there. This works pretty well!

However, we have made our tests take longer than they need to, and, crucially if the api ever takes longer than 10 seconds to resolve, our test will fail again. Perhaps this isn’t the best solution after all…

Complexify the app (commit)

We receive some requirements from our Product Owner, and it seems that a new feature is required where if an fact is at holyCrudThatIsInteresting level of interest, we should put a button up allowing the user to request more information about the fact.

We add the feature and look to update our e2e tests.

Now our app logic is more complicated, we need to account for this complexity in our e2e tests.

Test the new complexified app (commit)

We add a new test for the feature, but unfortunately it only works very very rarely, as if the random fact that comes back is not of the right interest level, our button is not shown, and the test fails. Disaster!

We could start to experiment with making our tests clever, and having them check for the interest level of the fact before progressing, but it is easy to see how an approach like that could blow up and become messy very quickly.

Enter the stub slow information service (commit)

We generate a new service, that implements the existing InformationService in the Angular application. Because it is strongly typed, it must follow the same interface, so we can be relatively confident that we shouldn’t be able to make any methods in our stub return nonsensical or invalid data.

This commit is the meaty part of this post. Here we use Angular’s environment files, as well as their dependency injection, to run our e2e tests with the StubSlowInformationService instead of the SlowInformationService.

Now our tests run, and they run quickly.

If the slow-ass-api implementation changes, our stub will stop compiling, and we know that we need to update our code. So this approach is relatively safe, assuming that you have a sufficiently well defined contract for how the api should behave (in this case our TypeScript Type definitions).

Hooray!

Even more control (commit)

Going one step further, in this commit, we expose methods on the window object, meaning that we can change the behaviour of our stub at run time, during our e2e tests.

Again, this is relatively safe if steps are taken to make sure the api contract is adhered to in the Angular application (by respecting the Types defined in the slow-ass-api library.

Conclusion

We have managed to write an e2e test which is quite robust, fast and easy to manage.

Due to fact that both our Angular application, and the slow ass api conform to a common contract about the behaviour of the API, we can be relatively confident that our tests are meaningful, and represent a realistic interaction with our API.

I think this is a pretty neat approach, and it has proved successful for me at work also. I’d be very keen to hear other peoples’ opinions though as e2e testing in general is something that I’ve found to be a seriously grey area in the frontend world.

HTF do I move from Angular components to React components

Angular has web components.

By that I mean a modular piece of front end UI code with the following:

  • HTML template
  • TypeScript class to handle UI logic
  • Encapsulated CSS

Angular’s web components also support one way data flow via Output and Input class decorators, and dependency injection.

So how do I Reactify these concepts?

Let’s start with a basic dumb Angular Component, with a form, an input value and an output emitter, and see if we can’t shiny it up with some React:

useful-form.component.ts

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';

export interface UsefulInformation {
  firstName: string;
  crucialInformation: string;
}

@Component({
  selector: 'app-useful-form',
  templateUrl: './useful-form.component.html',
  styleUrls: ['./useful-form.component.css']
})
export class UsefulFormComponent {
  @Input() name: string;
  @Output()
  usefulUserInformation: EventEmitter<UsefulInformation> = new EventEmitter<
    UsefulInformation
  >();

  public usefulForm: FormGroup;
  public crucialOptions = ['important', 'necessary', 'crucial'];

  constructor() {
    this.usefulForm = new FormGroup({
      number: new FormControl(this.name, Validators.required),
      crucialInformation: new FormControl('', Validators.required)
    });
  }

  public submit() {
    this.usefulUserInformation.emit(this.usefulForm.value);
  }
}

useful-form.component.html

<form [formGroup]="usefulForm">
  <h2>Hello <span *ngIf="!name">person</span>{{ name }}</h2>
  <label for="number">Number</label>
  <input
    id="number"
    type="number"
    formControlName="number"
    placeholder="pick a number!"
  >
  <label for="crucialInformation">Crucial information</label>
  <select
    id="crucialInformation"
    formControlName='crucialInformation'
  >
    <option
      *ngFor="let option of crucialOptions;"
      [value]="option"
    >
      {{ option }}
    </option>
  </select>
  <button
    (click)="submit()"
    [disabled]="usefulForm.invalid"
  >Submit</button>
</form>

So here we have a component that can be embedded in other HTML, with an input passed in via a name field, that we’re rendering in the template, an output event that can be listened to, and a reactive form. There is also an *ngFor and and *ngIf.

The component can be used like below in a parent component:

<app-useful-form
  name='Rob'
  (usefulUserInformation)="handleUserInformation($event)"
></app-useful-form>

All pretty standard stuff. Let’s try and replicate this behaviour in React.

Reactified useful component

First of all I want to roughly map some Angular concepts related to components, to their React equivalents:

useful-form.js

import React from 'react';

export default class UsefulForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      number: '',
      crucialInformation: ''
    };

    this.crucialOptions = ['important', 'necessary', 'crucial'];

    this.handleNumberChange = this.handleNumberChange.bind(this);
    this.handleCrucialInformationChange = this.handleCrucialInformationChange.bind(
      this
    );
  }

  handleNumberChange(event) {
    this.setState({ ...this.state, number: event.target.value });
  }

  handleCrucialInformationChange(event) {
    this.setState({ ...this.state, crucialInformation: event.target.value });
  }

  render() {
    return (
      <form onSubmit={() => this.props.handleSubmit(this.state)}>
        <h1>Hello {this.props.name || 'person'}</h1>
        <label>
          Number:
          <input
            type="number"
            required
            placeholder="pick a number!"
            value={this.state.number}
            onChange={this.handleNumberChange}
          />
        </label>
        <label>
          Crucial information:
          <select
            required
            value={this.state.crucialInformation}
            onChange={this.handleCrucialInformationChange}
          >
            <option value=''>Pick option</option>
            {this.crucialOptions.map(option => <option value={option} key={option}>{option}</option>)}
          </select>
        </label>
        <input
          type="submit"
          value="Submit"
          disabled={!(this.state.number && this.state.crucialInformation)}
        />
      </form>
    );
  }
}

The component is used as follows in a parent component:

class App extends Component {

  handleUsefulFormSubmit(event) {
    window.alert(JSON.stringify(event));
  }

  render() {
    return (
      <UsefulForm
        name="Rob"
        handleSubmit={this.handleUsefulFormSubmit}
      />
    );
  }
}

export default App;

What are the key differences/learnings?

  1. React seems to be much less opinionated about how you do things and more flexible. It is also JavaScript, where Angular is in many ways its own thing.
  2. Forms in React seem to require more work to achieve the same as in Angular (validation, disabled buttons, updates to form values etc.). I suspect as I mess around with this I will find some nicer patterns for handling programatic driven forms.

Overall the differences are not as great as I had feared. Both allow for controlling child components from a parent component, and flowing data into and out of the component without mutating things. Also the difference between *ngIf and *ngFor and just using interpolated JavaScript is very minimal.

I am pleasantly surprised by how much I like React so far…

WTF are Angular Schematics

  • Hate writing lots of repetitive boilerplate code?

  • Work with a large team and want to standardise/document common architectural patterns?

  • Want to get new developers up to speed quickly on best practices within your project?

YOU NEED CODE GENERATORS!!!

Code generators allow you to create code templates and folder structures for common tasks. For example in an Angular application, a code generator might have templates for creating components and services.

These are tasks that have to be carried out many many times in the implementation of a feature, and can be susceptible to different developers implementing them in different ways, resulting in time lost debating the best way to do things and/or confusion when reading other developers’ work.

With the addition of a generator, that has been agreed upon by the whole dev team, best practices within the project can be documented in the template, and another annoying decision/source of contention at the code review phase of a feature can be avoided. Hooray!

Generators can save a lot of time, and massively improve cohesion within a team. Obviously, as with anything, they can also make your life harder if they are not used properly.

If you use Angular, you may have used the Angular CLI (stands for Command Line Interface), which allows you to write things like

ng generate component my-component

inside a terminal in your project, and very rapidly have a skeleton my-component component made for you, with code and file names in line with Angular best practices, complete with working tests.

Very cool stuff. If you write Angular code and you don’t use it currently, I would STRONGLY recommend giving it a try

https://cli.angular.io/

Even if you do use the Angular CLI, what you might not know is that you can now write your own generators, using the same engine that the Angular CLI is written in, namely Angular Schematics.

Angular Schematics are fairly new, so the documentation is sparse and the API is a bit up in the air, but they are already quite nice to work with, with convenience methods for common Angular specific tasks, like adding module imports etc.

I personally have used them at work to streamline my team’s development experience when generating the vast amount of set up code that ngrx state management requires, and they have already saved a bunch of time and effort.

The schematics engine was designed with Angular in mind, but there is no reason you can’t use it to write generators for any code you feel like.

If these sound like things you might be interested in, then check out my schematics code here :

https://github.com/robt1019/ten-schematics

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