The basic components of a unit test in Angular

When I hear the phrase “unit test”, my thoughts typically gravitate toward backend systems. Unit testing is common and expected in backend systems but seems to be overlooked when discussing frontend development. However, frontend unit testing is not only beneficial for many reasons, but it’s also simpler than it might at first appear. In this introduction to unit testing in Angular, we’ll discuss why unit testing is worth it and then walk through the common components typically found in an Angular test file. What is a unit test? Unit tests aim to test small components of an application in isolation and typically compare actual behaviour with expected behaviour in order to verify that code works correctly. Benefits of unit testing in Angular Unit testing can: Aid in identifying bugs during development rather than in production Save time and money through the aforementioned early bug detection Help to maintain or improve code quality by encouraging developers to write more readable, maintainable, and reusable code Give developers confidence that new code additions are not breaking existing code Overall, unit testing is beneficial for any application. So, what does unit testing look like in Angular? Let's discuss that, starting with the files where the magic happens: spec files. Specification (spec) files If you use the Angular CLI to generate a new component, you will notice that four files are automatically generated: an html file, a css file, a typescript file, and lastly, a spec file. The spec file is where you’ll write the unit tests for the associated component. These tests are run using Jasmine, a JavaScript testing framework. As I mentioned before, unit testing for frontend is often overlooked. Applications don’t need the spec file to function, and so spec files are often deleted to avoid clutter. Let’s understand the contents of these files so that we can use them to improve our applications instead! The spec file generated alongside each Angular component looks something like this at first (borrowed from this website: https://esketchers.com/angular-unit-testing-using-jasmine-and-karma/#:~:text=What%20is%20Karma%3F,devices%20like%20phones%2C%20and%20tablets ) : import { TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ RouterTestingModule ], declarations: [ AppComponent ], }).compileComponents(); }); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); it(`should have as title 'AngularUnitTesting'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual('AngularUnitTesting'); }); }); When I first saw one of these files, I was quickly intimidated by the various blocks using keywords and function names I’d never seen before, such as ‘describe’, and ‘it’ - but things make a lot of sense when we break them down! But first, a bit more about the Jasmine framework If you’re looking into unit testing, there’s a good chance you’ve come across the term Test-Driven Development, or TDD. TDD involves writing tests for expected software functionality before developing said functionality and then making sure that when the functionality is implemented, the written tests (which should have been failing when first written) now pass. Jasmine is described as a Behaviour-Driven Development (BDD) framework. BDD is an extension of TDD, so, as with TDD, when following BDD methodology tests are often written before the functionality they’re intended to test. However, BDD differs from TDD in that BDD focuses on the behaviour of the application that the user expects. For example, say we have a method called add which adds two numbers together. A TDD-style test might ask, does add(2, 3) return 5? Whereas a BDD-style test would focus on the fact that when two numbers are added, the user should see their sum displayed. So, when writing Angular unit tests with Jasmine, we can expect to see some BDD-style traits. Now, onto that spec file breakdown! The basic functions and tools found in a spec file Have a look at this code snippet from the Jasmine documentation: describe("A suite is just a function", function() { let a; it("and so is a spec", function() { a = true; expect(a).toBe(true); }); }); Now let's walk through what we can see. it it is a global Jasmine function. It is used to define what are called specs (our tests). This function takes in a string and a function, where the string names the spec and the function is the spec itself. describe describe is use

Jan 17, 2025 - 12:57
The basic components of a unit test in Angular

When I hear the phrase “unit test”, my thoughts typically gravitate toward backend systems. Unit testing is common and expected in backend systems but seems to be overlooked when discussing frontend development. However, frontend unit testing is not only beneficial for many reasons, but it’s also simpler than it might at first appear. In this introduction to unit testing in Angular, we’ll discuss why unit testing is worth it and then walk through the common components typically found in an Angular test file.

What is a unit test?

Unit tests aim to test small components of an application in isolation and typically compare actual behaviour with expected behaviour in order to verify that code works correctly.

Benefits of unit testing in Angular

Unit testing can:

  • Aid in identifying bugs during development rather than in production
  • Save time and money through the aforementioned early bug detection
  • Help to maintain or improve code quality by encouraging developers to write more readable, maintainable, and reusable code
  • Give developers confidence that new code additions are not breaking existing code

Overall, unit testing is beneficial for any application. So, what does unit testing look like in Angular? Let's discuss that, starting with the files where the magic happens: spec files.

Specification (spec) files

If you use the Angular CLI to generate a new component, you will notice that four files are automatically generated: an html file, a css file, a typescript file, and lastly, a spec file. The spec file is where you’ll write the unit tests for the associated component. These tests are run using Jasmine, a JavaScript testing framework.

As I mentioned before, unit testing for frontend is often overlooked. Applications don’t need the spec file to function, and so spec files are often deleted to avoid clutter. Let’s understand the contents of these files so that we can use them to improve our applications instead!

The spec file generated alongside each Angular component looks something like this at first (borrowed from this website: https://esketchers.com/angular-unit-testing-using-jasmine-and-karma/#:~:text=What%20is%20Karma%3F,devices%20like%20phones%2C%20and%20tablets ) :

import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';

describe('AppComponent', () => {
 beforeEach(async () => {
   await TestBed.configureTestingModule({
     imports: [
       RouterTestingModule
     ],
     declarations: [
       AppComponent
     ],
   }).compileComponents();
 });

 it('should create the app', () => {
   const fixture = TestBed.createComponent(AppComponent);
   const app = fixture.componentInstance;
   expect(app).toBeTruthy();
 });

 it(`should have as title 'AngularUnitTesting'`, () => {
   const fixture = TestBed.createComponent(AppComponent);
   const app = fixture.componentInstance;
   expect(app.title).toEqual('AngularUnitTesting');
 });

});

When I first saw one of these files, I was quickly intimidated by the various blocks using keywords and function names I’d never seen before, such as ‘describe’, and ‘it’ - but things make a lot of sense when we break them down!

But first, a bit more about the Jasmine framework

If you’re looking into unit testing, there’s a good chance you’ve come across the term Test-Driven Development, or TDD. TDD involves writing tests for expected software functionality before developing said functionality and then making sure that when the functionality is implemented, the written tests (which should have been failing when first written) now pass.

Jasmine is described as a Behaviour-Driven Development (BDD) framework. BDD is an extension of TDD, so, as with TDD, when following BDD methodology tests are often written before the functionality they’re intended to test. However, BDD differs from TDD in that BDD focuses on the behaviour of the application that the user expects.

For example, say we have a method called add which adds two numbers together. A TDD-style test might ask, does add(2, 3) return 5? Whereas a BDD-style test would focus on the fact that when two numbers are added, the user should see their sum displayed. So, when writing Angular unit tests with Jasmine, we can expect to see some BDD-style traits.

Now, onto that spec file breakdown!

The basic functions and tools found in a spec file

Have a look at this code snippet from the Jasmine documentation:

describe("A suite is just a function", function() {
    let a;

    it("and so is a spec", function() {
        a = true;

        expect(a).toBe(true);
    });
});

Now let's walk through what we can see.

it

it is a global Jasmine function. It is used to define what are called specs (our tests). This function takes in a string and a function, where the string names the spec and the function is the spec itself.

describe

describe is used to group related specs. This group of related specs is referred to as a suite. Like the it function, describe is a function that takes in a string: here this string names the collection of specs.

When the string from the it function parameters is concatenated onto the end of the string from the describe parameters, you should be able to read it as you would an ordinary sentence. In this case, we’d read “A suite is just a function and so is a spec”. This sort of natural language definition of a test aligns with the principles of BDD, emphasizing clarity and readability.

Expectations and Matchers

As we mentioned earlier in this post, a unit test typically compares actual and expected values. In a Jasmine unit test, this comparison is achieved with the use of the expect function and what are called matcher functions.

The expect function takes in the “actual” value, and the matcher will take in the “expected” value. The matcher will use the actual and expected values to assert either true or false, and Jasmine will use this information to determine whether the spec should pass or fail.

A wide range of matchers exist to verify different things. For example, in the snippet above, the matcher “toBe” simply expects the actual value to be equal to the expected value. But say we want to compare numerical values with some leeway; we could use the "toBeCloseTo" matcher, which expects the actual value to be within a parameter-defined precision range of the expected value. Jasmine also caters for cases when a developer might like to write custom matchers for their own unique purposes.

Setup and Teardown

The last aspect of an Angular unit test that we'll be discussing are the functions made available for setup and teardown. The purpose of these functions is to prepare and clean up the environment for test execution. They help to ensure that specs are isolated, reproducible, and free from interference caused by the state left over from other specs. Jasmine provides a few functions to help developers with setup and teardown, namely:

  • beforeEach: called before every spec in the describe block in which it is used
  • afterEach: called after every spec in the describe block in which it is used
  • beforeAll: called once before any of the specs in the describe block are run
  • afterAll: called once after all of the specs in the describe block have run

Here's a code snippet to demonstrate some of beforeEach and afterEach in action (courtesy of the Jasmine documentation found at https://jasmine.github.io/archives/2.4/introduction )

describe("A spec using beforeEach and afterEach", function() {
var foo = 0;

beforeEach(function() {
  foo += 1;
});

afterEach(function() {
  foo = 0;
});

it("is just a function, so it can contain any code", function() {
  expect(foo).toEqual(1);
});

it("can have more than one expectation", function() {
  expect(foo).toEqual(1);
  expect(true).toEqual(true);
});
});

And, from the same source, beforeAll and afterAll in action:

describe("A spec using beforeAll and afterAll", function() {
var foo;

beforeAll(function() {
  foo = 1;
});

afterAll(function() {
  foo = 0;
});

it("sets the initial value of foo before specs run", function() {
  expect(foo).toEqual(1);
  foo += 1;
});

it("does not reset foo between specs", function() {
  expect(foo).toEqual(2);
});
});

Something important to note about beforeAll and afterAll is that although they may be useful to reduce the cost of setup and teardown, since they are only called once and are not reset between specs, using these functions can easily cause false positives and negatives in your specs due to state leakage from other specs.

Conclusion

This beginner's guide provided an explanation of what unit testing is and why it's just as important in frontend development as it is in backend development. We then walked through what a unit test looks like in Angular and discussed some of the common functions provided by the Jasmine framework. I hope this post has cleared up some of the confusion you might feel when encountering a spec file for the first time and inspired you to embrace thorough testing in your application!