By Danyal Zia | 1/31/2017 | General |Beginners

Writing Unit Tests in Angular 2

Writing Unit Tests in Angular 2

The testing of the smallest parts of the application through unit testing may seem strange to new developers.

When the applications reach the production level, then the developers start separating the smallest testable parts of an application for the testing (let’s say functions for printing a text on the console), as it assures that new changes in the development mode don’t break the core features. Usually, a small piece of source code from several source files are grouped together (especially in the case of unit testing) for the testing, and it happens mostly in automation.

Imagine a scenario where you have a functioning application that is hosted on Github for other developers to see, but it reached the production level and yet you still want to receive changes (in the form of pull requests) from other developers. Your application is now being used by major corporations or several commercial projects where even the slightest mistake in your code can break their application. In this scenario, only  unit-testing can provide you with a tool to evaluate the new changes if it matches with your previous desire or expectations of your application.

In AngularJS 2, testing is provided in two ways: unit testing and end-to-end testing. The top-down approach is end-to-end, where the tests are run against your application running in a real browser and so interacting with it as a user would. Testing in isolation is unit-testing in which we give certain input to the functions and evaluate the output to make sure it matches our expectations.

So, in this article, I am going to show how you can write unit tests through Jasmine and Karma. Let’s get started!

Writing Unit Tests in Angular 2 – Using Jasmine and Karma

When you create a new project through CLI, you will find the “e2e” folder that contains the end-to-end tests using the Protractor. You also get “karma.conf.js” and “protractor.conf.js” which are the configuration files for Karma and Protractor tests (we will seethis  later on). Protractor is nothing but a default end-to-end testing framework for AngularJS 2.

Testing guards you against the changes that break existing code (‘regressions’) So for example, if your application is open-source, and it gets new changes by the members of your team (through pull requests, for instance), then tests can help you determine if the specific changes are suitable for the development mode.

Pipes and Services should be tested with unit testing, but components need to be tested with Angular testing utilities (through the TestBed class and helper functions from @angular/core/testing), because components interact with other components, so unit tests can’t reveal that information to us.

We will focus on unit testing only in this article, but just to let you know that end-to-end testing does not replace unit testing, so both could work together. For example, in e2e testing, one process runs the real application, and the second process runs the protractor tests. Unit tests are isolated in the sense that they examine an instance of a class (or object) by itself without any reference to other dependencies, so you can test the important codes separately, meaning you can use both approaches in a same application.

Even though AngularJS 2 also provides its own Testing Utilities for creating a test environment, they are not very sophisticated compared to Jasmine, Karma and Protractor (for end-to-end). We won’t be writing end-to-end tests, but rather unit tests, although we will be using Angular Testing utilities nevertheless.

Using Karma (with Jasmine)

So, let’s write our first Karma test. The cool thing about Karma is that it creates a browser environment for the testing, so that you can test your code besides running your application. Due to the fact that Karma is highly sophisticated, AngularJS 2 made it the preferred test runner. You not only are able to test code in your browser, but also in other devices, like phones, tablets, etc. Karma uses Jasmine, but it can be replaced with other frameworks, like Mocha and QUnit.

Why Jasmine? Well, Jasmine is a behavior-driven development framework for testing the JavaScript code. It is usually used together with Karma, but the most important thing about Jasmine is that it is dependency free and doesn’t even require a DOM! The provided spy function helps in telling how many times the function is called or not, and many more features!

Create a new project:

$ng new AngularJS2-Testing

Make sure to install the Karma CLI:

$npm install -g karma-cli

Create a new file “sampletest.spec.ts” in the “app/” folder and copy the following contents:

sampletest.spec.ts

 

describe('Sample Test', () => {
 it('true is true', () => expect(true).toBe(true));
});

import {AppComponent} from './app.component';

describe('AppComponent', () => {
 beforeEach(function() {
   this.app = new AppComponent();
 });

 it('should have hello property', function() {
   expect(this.app.hello).toBe('Hello, World!');
 });
});

In your “app.component.ts”, add the following property:

private hello: string = 'Hello, World!';

This is nothing but a simple Boolean test. We have created the file with extension “.spec.ts” because this is what is expected by Karma configuration. Now do the “ng test” in the root folder, it will open the browser and show you the Karma test screen. You will notice “Debug” button, but ignore it for now. Now change the “expect(true)” to “expect(false)” and you will see the errors on the console screen.

Why are we getting errors? Because “true is true” is logically valid, however, we are expecting the value of “false”. If you revert it again to “true”, the errors will be gone. The same for the next describe method, you can change the text to anything but “Hello, World!”, and it will show errors.

Through simple methods, you can test several components as well. For example, when you create new components through Angular CLI, it automatically creates the test files (.spec.ts) for the component, which does nothing but test if the components are working correctly together with other components.

The spec files are unit tests for your source files. The convention for Angular2 applications is to have a .spec.ts file for each .ts file. They are run using the Jasmine JavaScript test framework through the Karma task runner when you use the 'ng test' command.

In order to config our Karma, we need to create a new file “karma.conf.js” or modify the existing one. The default one has the following content:

karma.conf.js

 

// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
 config.set({
   basePath: '',
   frameworks: ['jasmine', 'angular-cli'],
   plugins: [
     require('karma-jasmine'),
     require('karma-chrome-launcher'),
     require('karma-remap-istanbul'),
     require('angular-cli/plugins/karma')
   ],
   files: [
     { pattern: './src/test.ts', watched: false }
   ],
   preprocessors: {
     './src/test.ts': ['angular-cli']
   },
   remapIstanbulReporter: {
     reports: {
       html: 'coverage',
       lcovonly: './coverage/coverage.lcov'
     }
   },
   angularCli: {
     config: './angular-cli.json',
     environment: 'dev'
   },
   reporters: ['progress', 'karma-remap-istanbul'],
   port: 9876,
   colors: true,
   logLevel: config.LOG_INFO,
   autoWatch: true,
   browsers: ['Chrome'],
   singleRun: false
 });
};

Here, we are telling that the PhantomJS browser will be used, Jasmine testing framework will be used and the Webpack for bundling the files. You can read more about File Patterns and other options here (http://karma-runner.github.io/1.0/config/configuration-file.html).

Testing Components

We will be using the TestBed function for testing the component, which is the main entry to all of Angular's testing interface. It will let us create our components, so they can be used to run unit tests. You can configure the class with configureTestingModule, which basically takes @NgModule-like metadata object (i.e., declaration, import, etc.). Sounds like mumbo-jumbo? Well, let’s write some code then!

 

Create a new component “sample”:

$ng g component sample

 

It will create both the component file and its respective .spec.ts file. We will modify the content a bit. Copy the following code to the files:

 

sample.component.ts

 

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

@Component({
 selector: 'app-sample',
 templateUrl: './sample.component.html',
 styleUrls: ['./sample.component.css']
})
export class SampleComponent implements OnInit {
 title = 'Test Sample Component';
  constructor() { }
 ngOnInit() {
 }
}

sample.component.spec.ts

 

 

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By }              from '@angular/platform-browser';
import { DebugElement }    from '@angular/core';
import { SampleComponent } from './sample.component';
describe('SampleComponent (inline template)', () => {
 let component:    SampleComponent;
 let fixture: ComponentFixture<SampleComponent>;
  // For Debugging HTML Elements
 let debug:      DebugElement;
 let htmlElem:      HTMLElement;
 beforeEach(() => {
   TestBed.configureTestingModule({
     declarations: [ SampleComponent ], // Our Test sample component
   });
// Get the ComponentFixture
   fixture = TestBed.createComponent(SampleComponent);
   component = fixture.componentInstance; // SampleComponent test instance
   // CSS Element selector
   debug = fixture.debugElement.query(By.css('h1'));
   htmlElem = debug.nativeElement;
 });
 it(‘don’t show any title on DOM until we call `detectChanges`', () => {
   expect(htmlElem.textContent).toEqual('');
 });
 it('should display original title', () => {
   fixture.detectChanges();
   expect(htmlElem.textContent).toContain(component.title);
 });
 it('should display a different test title', () => {
   component.title = Different Test Title';
   fixture.detectChanges();
   expect(htmlElem.textContent).toContain('Different Test Title');
 });
});

Here, we are using createComponent in TestBed which does nothing but create an instance of the component. This returns ComponentFixture which is nothing but a handle that surrounds the created component.

 

These tests tell Angular when to perform change detection through fixture.detectChanges() (which we received from createComponent). TestBed.createComponent by default doesn’t trigger the change detection. This is why specific parts in our test won’t show the changes on the DOM.


In order to apply auto detection globally, you need to use ComponentFixtureAutoDetect from ‘@angular/core/testing’. So, in your configureTestingModule:

 

 

TestBed.configureTestingModule({
 declarations: [ SampleComponent ],
 providers: [
   { provide: ComponentFixtureAutoDetect, useValue: true }
 ]
})

For the testing on Forms, you can check out this (https://github.com/gonzofish/semaphore-ng2-webpack) project for awesome form test scripts! You can test the Pipes and services in the same manner, just import the component(s), and then pass the values to the expect() method and see the magic!

Basically, in Karma, the testing is all about giving expectations and watching if the functions return the value as per the expectation.

Conclusion

From all of this, you can see how easy it is for you do the unit testing in AngularJS 2. It not only helps you in figuring out the errors in your applications, but it also builds your organizational skills and shows how you can better organize your code, thus making you not just a better programmer, but also a better writer of code!

If you have any questions, please ask in the comment section below! And be sure to stop by the homepage to search and compare the best SDKs, APIs, and other development tools.

By Danyal Zia | 1/31/2017 | General

{{CommentsModel.TotalCount}} Comments

Your Comment

{{CommentsModel.Message}}

Recent Stories

Top DiscoverSDK Experts

User photo
3355
Ashton Torrence
Web and Windows developer
GUI | Web and 11 more
View Profile
User photo
3220
Mendy Bennett
Experienced with Ad network & Ad servers.
Mobile | Ad Networks and 1 more
View Profile
User photo
3060
Karen Fitzgerald
7 years in Cross-Platform development.
Mobile | Cross Platform Frameworks
View Profile
Show All
X

Compare Products

Select up to three two products to compare by clicking on the compare icon () of each product.

{{compareToolModel.Error}}

Now comparing:

{{product.ProductName | createSubstring:25}} X
Compare Now