In this Angular 2.0 article series, we have already discussed about different types of basic and advanced concepts or features of AngularJS 2.0 like Data binding, Directives, pipes, Service, route, http modules, pipes, change detection, state management, Lazy loading of the modules, Localization including implementations of amcharts library in AngularJS 2.0. Now, in this article, we will discuss about one of the main advantage of angularjs i.e. Unit Testing.
In case you have not had a look at the previous articles of this series, go through the links mentioned below.
Nowadays, JavaScript has become the de facto programming language to build and empower frontend/ Web AZpplications. We can use JavaScript to develop simple or complex applications. However, applications in production are often vulnerable to bugs caused by design inconsistencies, logical implementation errors, and similar issues. For this reason, it is usually difficult to predict how applications will behave in real-time environments, which leads to an unexpected behavior, non-availability of the Applications, or outages for short or long durations. This generates lack of confidence and dissatisfaction among application users. Also, high cost is often associated with fixing the production bugs. Therefore, there is a need to develop applications that are of a high quality and that offer high availability.
Test-Driven-Development is an engineering process in which the developer writes an initial automated test case that defines a feature, subsequently writes the minimum amount of code to pass the test and eventually refactors the code to the acceptable standards.
A Unit test is used to test individual components of the system. An integration test is a test, which tests the system as a whole and how it will run in production. Unit tests should only verify the behavior of a specific unit of code. If the unit's behavior is modified, then the unit test would be updated as well. Unit tests should not make assumptions about the behavior of other parts of your codebase or your dependencies. When other parts of your codebase are modified, your Unit tests should not fail. (Any failure indicates a test that relies on other components and is therefore not a Unit test.) Unit tests are cheap to maintain and should only be updated when the individual units are modified. For TDD in Angular, a unit is most commonly defined as a class, pipe, component or Service. It is important to keep units relatively small. This helps you to write small tests, which are self-documenting, where they are easy to read and understand.
The Testing Tool chain
Our testing toolchain will consist of the tools given below.
- Jasmine
- Karma
- Phantom-js
- Istanbul
- Sinon
- Chai
Jasmine is the most popular testing framework in Angular community. This is the Core framework that we will write our Unit tests with.
Karma is a test automation tool for controlling the execution of our tests and what Browser to perform them under. It also allows us to generate various reports on the results. For one or two tests, this may seem like overkill, but as an Application grows larger and the number of units to test grows, it is important to organize, execute and report on the tests in an efficient manner. Karma is library agnostic, so we can use other testing frameworks in combination with other tools (like code coverage reports, spy testing, e2e etc.)
In order to test our Angular Application, we must create an environment for it to run in. We can use a Browser like Chrome or Firefox to accomplish this (Karma supports in-Browser testing) or we can use a Browser-less environment to test our Application, which can offer us greater control over automating certain tasks and managing our testing Workflow. PhantomJS provides a JavaScript API that allows us to create a headless DOM instance, which can be used to Bootstrap our Angular Application. Use this DOM instance that is running our Angular Application. We can run our tests.
Istanbul is used by Karma to generate code coverage reports, which tells us the overall percentage of our Application being tested. This is a great way to track which components/Services/ pipes etc. have tests written and which don't. We can get some useful insights into how much of the Application is being tested and where.
For some extra testing functionality, we can use the Sinon library for things like test spys, test subs and mock XHR requests. This is not necessarily required as Jasmine comes with the spyOn function for incorporating spy tests.
Chai is an assertion library that can be paired with any other testing framework. It offers some syntactic sugar that lets us write our unit tests with different verbiage - we can use a should, expect or assert interface. Chai also takes advantage of function chaining to form English-like sentences used to describe tests in a more user friendly way. Chai isn't a required library for testing and we won't explore it much more in this handout, but it is a useful tool to create cleaner, more well-written tests.
Filename conventions
Each Unit test is put into its own separate file. Angular team recommends putting Unit test scripts alongside the files. They are testing and using a .spec filename extension to mark it as a testing script (this is a Jasmine convention). Hence, if you had a component /app/components/mycomponent.ts, then your Unit test for this component would be in /app/components/mycomponent.spec.ts. This is a matter of personal preference; you can put your testing scripts wherever you like, though keeping them close to your source files makes them easier to find and gives those, who aren't familiar with the source code an idea of how that particular piece of code should work.
How to install Jasmine on Windows
To install Jasmine on Windows machine, we need to first install node.js. Since we are using TypeScript to develop Angular 2.0 code, which means Node JS environment is already set in the development machine. Thus, now we can directly install Jasmine by using command line arguments.
Step 1
npm -- version (for checking npm is properly installed or not).
Step 2
npm install –g yo (for install yomen).
Step 3
There is another way of using Jasmine. For it, we can install karma framework, using npm by using the command given below.
npm install –g karma karma-cli jasmine-core
Now, after it, go the project directory by browsing and initializing the configuration. For this karma, ask few simple questions and on the basis of the given answer, it will create the configuration files.
karma init jasmine.config.js
After completing the configuration file creation, we just need to start the karma by passing the configuration file name as the arguments.
karma start jasmine.config.js
Now, the environment to run theJasmine test is ready. Now, there is a need to run Jasmime test. For this, we create a folder named test within the project directive and add one html file called index.html for running the jasmine test file. Now first we will create a simple JavaScript function which takes two number arguments and return the total of two numbers. For this, we need to add another JavaScript file within the same folder named sampletest.js and write down the below code –
Also, we need to another JavaScript file to write down the code of Unit test. For this, we add another JS file test.js and write down the code given below. In this file, we add two scenarios where one is for correct scenario and another is wrong scenario i.e. expected and given data does not match.
Now, we add need to add the code given below in the index.html file.
When I write Unit tests, I follow a pattern called arrange/act/assert (A/A/A). Arrange refers to the process of setting up the scenario required for the test. Act refers to performing the test itself and assert refers to checking the result to make sure it is correct. Jasmine tests are written, using JavaScript functions, which makes writing tests a nice extension of writing an Application code. There are several Jasmine functions in the example, which I have described below.
Name |
Descriptions |
describe |
Groups a number of the related tests (This is optional, but it helps to organize test code). |
beforeEach |
Executes a function before each test (This is often used for the arrange part of a test). |
it |
Executes a function to form a test (The act part of the test). |
expect |
Identifies the result from the test (part of the assert stage). |
toEqual |
Compares the result from the test to the expected value (the other part of the assert). |
The basic sequence to pay attention to is that the function executes a test function, so that the expect and toEqual functions can be used to assess the result. The toEqual function is only one way that Jasmine can evaluate the result of a test. I have listed the other available functions given below.
Name |
Descriptions |
expect(x).toEqual(val) |
Asserts that x has the same value as val (but not necessarily the same object). |
expect(x).toBe(obj) |
Asserts that x and obj are the same object. |
expect(x).toMatch(regexp) |
Asserts that x matches the specified regular expression. |
expect(x).toBeDefined() |
Asserts that x has been defined. |
expect(x).toBeUndefined() |
Asserts that x has not been defined. |
expect(x).toBeNull() |
Asserts that x is null. |
expect(x).toBeTruthy() |
Asserts that x is true or evaluates to true. |
expect(x).toBeFalsy() |
Asserts that x is false or evaluates to false. |
expect(x).toContain(y) |
Asserts that x is a string that contains y. |
expect(x).toBeGreaterThan(y) |
Asserts that x is greater than y. |
To perform a simple unit test, I first write down a simple TypeScript function, which adds two numbers.
Now, write down the code given below, as per the file.