DEV Community

Cover image for Simplify your Angular Component testing
Achilles Moraites
Achilles Moraites

Posted on

Simplify your Angular Component testing

Components are everywhere in our apps and testing them is part of our daily software development process.

But here are some good news: Testing should not be hard!

There are 3 types of tests for our Components:

  • Isolated (fastest)
  • Shallow (fast)
  • Deep (not so fast)

In this tutorial we will explore Isolated tests

Isolated Tests

Those are the less utilized tests in Angular testing yet the most fast and simple to use.

In those kinds of tests we treat the Component as a simple class.
Testing classes is easy, we can call their methods and verify the results.

Note that we just test the class functionality and NOT anything in the DOM.

Case 1 (simple) the component has no dependencies

Here is our component:

// explorer.component.ts

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

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

  explorerName: string;
  location: string;
  constructor() { }

  ngOnInit() {
  }

  getLocation(location) {
    this.location = `Your location is ${location}`;
  }

}
Enter fullscreen mode Exit fullscreen mode

Here we perform tests:

// explorer.component.spec.ts

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

import { ExplorerComponent } from './explorer.component';



describe('Isolated ExplorerComponent Tests', () => {
  let component: ExplorerComponent;
  beforeEach(() => {
    component = new  ExplorerComponent();
 });

  it('should set the correct location', () => {
   component.getLocation('Mars');
   expect(component.location).toContain('Mars');
 });

});
Enter fullscreen mode Exit fullscreen mode

We can test it just like any other class!

Case 2 : the component has dependencies

here is our service:

// explorer.service.ts

import { Injectable } from '@angular/core';
import { of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ExplorerService {

  constructor() { }

  getExplorer() {
    return of('Josh the Lost');
  }

  getLocation() {
    return 'The dark side of the moon';
  }
}
Enter fullscreen mode Exit fullscreen mode

our component:

// explorer.component.ts

import { Component, OnInit } from '@angular/core';
import { ExplorerService } from './services/explorer.service';

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

  explorerName: string;
  location: string;
  constructor(
    private explorerService: ExplorerService
  ) { }

  ngOnInit() {
    this.explorerService.getExplorer().subscribe(data => {
      this.explorerName = data;
    });
  }

  getLocation(location) {
    this.location = `Your location is ${location}`;
  }

}

Enter fullscreen mode Exit fullscreen mode

our tests:

// explorer.component.spec.ts

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

import { ExplorerComponent } from './explorer.component';
import { of } from 'rxjs';



describe('Isolated ExplorerComponent Tests', () => {
  let component: ExplorerComponent;
  // create a spyObject to simplify our mock service creation
  const mockExplorerService = jasmine.createSpyObj(['getExplorer']);
  // set the getExplorer method to return an Observable just like the real one
  mockExplorerService.getExplorer.and.returnValue(of('Dave the brave'));

  beforeEach(() => {
    component = new  ExplorerComponent(mockExplorerService);
 });

  it('should set the correct location', () => {
   component.getLocation('Mars');
   expect(component.location).toContain('Mars');
 });

  it('should set the correct explorer', () => {
  // trigger
  component.ngOnInit();

  expect(component.explorerName).toEqual('Dave the brave');
  // verify that getExplorer from the service has been called
  expect(mockExplorerService.getExplorer).toHaveBeenCalled();
});

});
Enter fullscreen mode Exit fullscreen mode

Conclusion

Using Isolated tests we can test functionality with a simple way that is performant and easy to understand.

Note that this approach is great for testing just the component (class) code and is not meant to replace TestBed in any way.
We can just combine different kinds of tests to compliment our testing efforts :)

Happy testing !!!

Photo by Rodolfo Clix from Pexels

Top comments (0)