DEV Community

Victor Leung
Victor Leung

Posted on • Originally published at Medium on

Writing unit test cases with Karma for angular component

I would like to convince you to start writing unit test cases for your angular web app. Shorten time to production is not an excuse to accumulate technical debts. There are many good reasons why you should start writing it:

  1. It helps you identify issues as soon as possible, especially when many different teams working on the same code base and breaking each other changes. It’s not fun to get an urgent call at mid-night for production support, then realized a bug was released to production.
  2. It helps you write better code as you can refractor with confidence that your app still working as expected. You can think and break down your code into small units, which is more manageable than a monolithic system.
  3. It may be simply your company policy which requires you to get a code coverage of 80% or above.

No matter the reasons, you need to know-how as well, as I notice that many new developers have no idea nor intention on doing it. If you are working on angular io project, it’s very easy to get started. Simply run in your project:

npm run test
Enter fullscreen mode Exit fullscreen mode

Then a Chrome browser is open at localhost with port 9876.

Clicked on the “Debug” button and you can start testing:

Right now nothing get runs as we haven’t started writing our test case yet. We can start writing cases to cover small isolated pieces of code, such as your method call. Don’t rely on manual testing to discover problems in your code as it’s your responsibility as a developer for code quality.

Let’s say, for example, you have a login.component.ts , which has a logon method. After it’s triggered, it would update a boolean flag from false to true:

export class LoginComponent {

  isLogon = false;

  login() {
      this.isLogon = true;
  }
}
Enter fullscreen mode Exit fullscreen mode

Then you can create a file with name login.component.spec.ts, which would be used for our test cases. After that, write your first test case:

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 able to logon', () => {
    component.login();
    expect(component.isLogon).toBeTruthy();
  })
});
Enter fullscreen mode Exit fullscreen mode

The test suits are inside your describe ( ) and each test case we wrote is inside the it ( ), and expect the isLogon flag is true after the logon method is triggered. Don’t worry about the rest, for now, restart the Chrome browser:

Your first test case is passed! Later on, in case some new joiner developer changed your code and modify it as below:

export class LoginComponent {

  isLogon = false;

  login() {
      this.isLogon = false;
  }
}
Enter fullscreen mode Exit fullscreen mode

You would be able to see your test case failed:

Okay, the above test case is trivial. In real world, we often make an API call to fetch some data from server. Remember, don’t call the real API to fetch data in your test case! Always mock your API call with stub data returned instead. To see how it works, let’s rewrite our simple logon component with service call:

import { AuthenticationService } from '../../services/authentication.service';

export class LoginComponent {

  constructor(private authenticationService: AuthenticationService { }

  isLogon = false;

  login() {

    this.authenticationService.login().subscribe(
                data => {
                  this.isLogon = true;
                },
                error => {
                  this.isLogon = false;
                });
  }
}
Enter fullscreen mode Exit fullscreen mode

We injected an authentication service into our component, and call the API, if it’s a success, it would update the isLogon boolean flag to true.

Now, let’s re-run our test, you would hit error, because we haven’t injected the service to our testing component yet. We can re-write our test case like this:

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

import { LoginComponent } from './login.component';
import { AuthenticationService } from 'src/app/services/authentication.service';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';

let stubData = {
  'username': 'testing'
};

class FakeAuthenticationService {

  login(){
    return Observable.of(stubData)
  }
}

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;

  let newFakeAuthenticationService = new FakeAuthenticationService();

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
      providers: [
        AuthenticationService
      ]
    })
    TestBed.overrideProvider(AuthenticationService, {useValue: newFakeAuthenticationService});
    TestBed.compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should able to logon', () => {
    component.login();
    expect(component.isLogon).toBeTruthy();
  })
});
Enter fullscreen mode Exit fullscreen mode

Here we add the provider Authentication Service at the configuration. Then we override the AuthenticationService with our fake AuthenticationService. The fake AuthenticationService simply return an Observable of stub data. Try to run our test case again and it passed!

This is just a demo case and there are better ways to write it, such as Jasmine createSpyObj and HttpClientTestingModule. But for simplicity, I just want to convince you to start writing unit test cases instead of scare you away.

Originally published at https://victorleungtw.com on August 7, 2020.

Top comments (0)