Search⌘ K
AI Features

Write a Unit Test

Explore writing unit tests for Angular components with Jasmine. Understand how to use TestBed to create isolated test environments, run change detection cycles, and make assertions on component properties and rendered HTML content. This lesson teaches you how to validate component creation and data binding effectively to improve application reliability.

Testing an Angular component

Let’s take a look at the files created when we generate a new component using the Angular CLI. For example, we could use the generate command to create an ArticlesIndex component:

Shell
ng generate component ArticlesIndex

When Angular generates the component, it creates four files:

Javascript (babel-node)
articles-index.component.html
articles-index.component.spec.ts
articles-index.component.ts
articles-index.component.css

Angular generates a basic test for us in articles-index.component.spec.ts. At the end of the file, there is an example test that Angular has written for us. The toBeTruthy method tests that our result is true when interpreted as a boolean. All values are considered to be truthy except for the following falsy values: false, 0, -0, "", null, NAN, and undefined. So, in this case, our component variable will be truthy if it was created successfully.

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

Try running your first Angular component test in the code widget below:

Angular component test

Remember that our goal in unit testing is to isolate the component or part of the system that we want to test. In this case, Angular provides us with a tool called TestBed, which we can use to do just that. TestBed lets us mount the component in a controlled environment. Instead of running the whole application, we fire up the component we are testing in isolation. Let’s take a look at the first beforeEach block:

Javascript (babel-node)
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ArticlesIndexComponent ]
})
.compileComponents();
});

First, we call configureTestingModule, where we declare the component we want to set up in the TestBed. At this stage, we haven’t created the component yet; we are simply declaring it so that we can use it later. Next, we call the compileComponents method, which runs asynchronously. Angular compiles our component by pulling in the external HTML and CSS files. In some setups, Angular compiles components on the fly; however, we need to do this explicitly in our test setup. We have used the async/await notation for this method so that our test runner will wait for the component to compile before moving on to the next step.

Now, let’s take a look at the next beforeEach block:

Javascript (babel-node)
beforeEach(() => {
fixture = TestBed.createComponent(ArticlesIndexComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

This is the block where we create our component under test. The fixture variable gives us access to the component we are testing. It provides us with some helpful ways to interact with the test component. The fixture also gives us direct access to the component via the componentInstance property. We need to call the fixture.detectChanges method, which triggers an Angular change detection cycleAngular uses a change detection cycle to re-render the DOM. We need to run a change detection cycle to see data updates reflected in the DOM.. The Angular change detection cycle takes the data from our component and binds it into the HTML template. We need to call the fixture.detectChanges function at the start of most tests. We might also need to rerun it during the test if we make data changes, which we want to see reflected in the component template.

Testing HTML content

Now, let’s look at our second example, where we render some data in the component and make assertions about the resulting HTML:

Testing Angular component HTML

Let’s break down what’s happening here.

  • On line 23, we use the fixture to give us access to the debugElement. The debugElement is designed as a testing helper, making it easy to inspect the HTML rendered by the component.

  • On line 31, we set the articles variable on the component, filling it with an array of dummy data. We provide two articles each with a title and description property.

  • On line 41, we run the fixture.detectChanges method, which triggers an Angular change detection cycle and re-renders the component HTML. We need to run this method so that the articles data array we loaded will be reflected in the DOM.

  • On line 43, we use the queryAll method to write a query that selects all the HTML elements with a class of title:

Javascript (babel-node)
const titles = debugElement.queryAll(By.css('.title'));

We can use the debugElement.query method to select the first matching element, or we can use the debugElement.queryAll method to select all matching elements. Then, we need to use the By.css method to run our CSS selector. Then we finally make our test expectations using the selected data:

Javascript (babel-node)
expect(titles[0].nativeElement.textContent).toEqual('JavaScript updates');
expect(titles[1].nativeElement.textContent).toEqual('Angular tips');

We used the .nativeElement property to access the underlying HTML element. This allows us to make our expectations about the .textContent of those elements.

Coding exercise

In this exercise, we test an Angular component called DrinksMenuComponent. The component has a property called drinks, defined on line 14, which stores an array of objects representing drinks from a menu. The component will render an HTML list of drinks and their prices.

In the file drinks-menu.component.spec.ts, your task is to complete the test should display the drink names.

On line 43, write code to select the HTML elements with the class of name and assert that they contain the correct values:

Coding exercise: testing rendered HTML

You can see the solution to the problem above in the code snippet below:

TypeScript 3.3.4
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { DrinksMenuComponent } from './drinks-menu.component';
describe('DrinksMenuComponent', () => {
let component: DrinksMenuComponent;
let fixture: ComponentFixture<DrinksMenuComponent>;
let debugElement: DebugElement;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DrinksMenuComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DrinksMenuComponent);
component = fixture.componentInstance;
fixture.detectChanges();
debugElement = fixture.debugElement;
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display the drink names', () => {
component.drinks = [
{
name: 'Americano',
price: 150
},
{
name: 'Macchiato',
price: 180
},
];
fixture.detectChanges();
const names = debugElement.queryAll(By.css('.name'));
expect(names[0].nativeElement.textContent).toEqual('Americano');
expect(names[1].nativeElement.textContent).toEqual('Macchiato');
});
});

We now know how to use TestBed to test an Angular component. The TestBed allows us to mock dependencies and query the rendered HTML.