Introduction to unit tests in Angular

Many times I hear this story:
- Should I test my Angular application?
- Yes, you should.
- Why?
- Because it accelerates development.
- Hmmm.. How?
- You don't need to test all the methods manually. You can run one command in the terminal and you see if something fails.

OK, so what is “automated testing”? It’s a good practice of writing code to test the code and do it in an automated way.

Imagine that you have simple functions that you use in your application in many places. To test it, you would have to run your application. Maybe you need to log in and then you have to manually test all the places where you used this function and see whether it returns correct results. If you have automated tests, the only thing you need to do is to run a command in the terminal and you can see if everything works correctly.

Yes, that's true that your development will be -  to some extent - a little bit longer. But if something fails you can find the place in your application that needs fixing much faster.

Running automated tests in Angular is easy. If you created your application with Angular CLI, you don't need to do anything. If you want to start some application with Angular CLI you need to run a command:

ng new my-dream-app

where “my-dream-app” is a name of your application and that’s it!

Types of tests

In general, we can distinguish three main types of tests:

Unit tests - as the name implies, it's about testing individual units of code. They try to answer many questions e.g. "Is the logic in my function correct?" or "Will I get the right results?". It’s important that we test our component in isolation without external resources (e.g. API endpoints, file system). Unit tests are easy to write and are extremely fast. When we are writing a unit test, we need to remember that we don’t have a template for them.

Integration tests - used to test a component with external resources (e.g. API endpoints, file system). To run them in Angular we need to look not for a Typescript class but for the whole component.

End-to-end tests - tests that make sure that the entire application works as expected from the user's perspective (we need to simulate a real user). These tests have one weaknesses - they are very slow.

In this post I will focus on the unit tests only.

How should I write a unit test?

To test an Angular application there are several tools that will simplify the configuration process and its execution. However, if we are using Angular CLI to create our applications, we use Karma and Jasmine by default.

Karma is a JavaScript command line tool that can be used to run a web server. It will load your application's source code and run your tests. As the creators write on their website: “The main goal for Karma is to bring a productive testing environment to developers.” You can configure Karma to work with different browsers. It is useful to ensure that the application works on all the browsers that you want it to. Karma will display the results of your tests in the command line once they have finished their job in the browser. It’s a NodeJS application, so you can install it through npm or yarn.

Jasmine - as is says on their official website, - “Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.” Today, it has become the most popular choice for testing Angular applications. It will help you keep your code well structured and documented.

To run a test environment, we need to type the following in our console:

ng test

When you run this command, Chrome will also open a new window with a list of your tests.


Now we can start our first test. Remember that when you write a test your functions/methods should be small - 10 lines of code or less. You should also remember to give your tests and methods names that perfectly describe what they do. It will help you find all errors quickly.

Below we have a simple component to display a post and a method to like this post.

import { Component } from '@angular/core';

@Component({
  selector: 'app-post',
  templateUrl: './post.component.html',
  styleUrls: ['./post.component.scss']
})
export class PostComponent {
  post = {
    id: 1,
    title: 'Title',
    content: 'Proin ut enim non nisl porta sollicitudin hendrerit sed arcu.',
    like: 1
  }

  likePost() {
    return this.post.like++;
  }
}

Now we can create a file for the test. Our component file is post.component.ts, so we need to create a file called: post.component.spec.ts. Karma - our test runner - will automatically look for files with .spec extension.
When we create our component with Angular CLI generator, this file should already exist.

Syntax

There are two functions that we will be using most of the time:

  • describe() - in Jasmine we use the describe function to group our tests together
  • it() - defines a spec or a test

We can describe our test like this (I will not focus on beforeEach sections with dependencies):

describe('PostComponent', () => {

})

The second argument here is a function that the test runner is going to call. Now we can write our spec/tests. While writing a unit test, we need to test all execution paths as well as import and define our component. To do this, we can use beforeEach function and name our first test.

import { PostComponent } from './post.component';

describe('PostComponent', () => {
    let component: PostComponent;
    
    beforeEach(() => {
        component = new PostComponent();
    });
    
    it('should increase post likes', () => {
    
    });
});

Now it’s time to run our function from the component and check if post likes will increase.

import { PostComponent } from './post.component';

describe('PostComponent', () => {
    let component: PostComponent;
    
    beforeEach(() => {
        component = new PostComponent();
    });
    
    it('should increase post likes', () => {
        component.likePost();
        expect(component.post.like).toBe(2);
    });
});

As we can see, everything is OK - our test works.

I hope that those of you who do not test your applications will start doing so and find this knowledge useful.

Tip

Did you know that you can check the coverage of your tests? To do this you need to run your tests with a flag:

ng test --code-coverage

Then you need to go to your project and you can see a new folder called "Coverage". You should find index.html and open it in your browser. All the functions in your application that are covered will be displayed there.