Disclaimer: The code examples in the article are using Angular2 RC4. The Angular2 developers made some serious changes in RC5 in connection with service injection that results in breaking my code. I have no time to maintain or upgrade my examples for RC5, but some github guys already started to look after the problem, so check my repo for further updates, or use RC4.
In the last few weeks I had the chance to experiment with Angular2 framework as part of my Summer Internship. I have encountered some difficulties setting up a fresh Angular2 project as well as configuring it to work well with Mocha Unit test framework. My intention is to share my experience with you and to provide some code and explanation in order for you to be able to bootstrap your project and test your code without difficulties.
The first step is the hardest
First thing first clone the repository for an easy start:
As the cloning finished run the following command:
$ npm install
After npm has finished installing all the dependencies you either run
$ npm start
command to launch lite-server or run the Unit tests with
You are all set. Was it difficult? :)
Bad things first — Out of the comfort zone
One interesting thing to note in package.json that 18 out of 21 dependencies are just for Angular2. ( Yes, you need to include these libs in your html too one by one ). I hope that they will somehow pack them into one in the future somehow.
Our main logic
You should note that the primary “language” of Angular2 is TypeScript and the author is also using TypeScript
I wouldn’t want to waste time on talking about TS compiling, for that you should read the Angular2 quickstart tutorial. Its enough to know for now that systemjs.config.js, tsconfig.json, typings.json are responsible for module loading and TS compiling.
Let’s see the main component of our project:
What you see above is a standard Angular2 Component with a backing “exported class” that has a Service injection and two “main” methods that process the data provided by the Service. If Service is an unknown concept to you then you should get to know it here. As soon as Angular initializes the data-bound input properties the data provided by our Service gets stored by our class and gets displayed in the browser. The function getData simply passes the response provided by Service to a callback method, we will see its use later. Now come and meet with the Service itself:
As you may realized I am imitating a http service here with mock data returning it with a Promise. Nothing magical happens here really.
If you previously launched the website with lite-server or any web server, you should see the following:
As you can see the data got pulled from the Service and angular works his magic with the rest. Our app is working like charm, we can start to Unit test the functionality. Great.
Following the untrodden path
It is good practice to name your test files with the following convention:
Let’s say we have app.component.ts, then our test file for AppComponent should be called app.component.test.ts. As an alternative you can use the world spec instead of test as an other popular test framework called Jasmine uses the spec convention.
You see all those imports from line 1 to 11? This import hell consists of JS, Node and TS style imports. Thank to the compiler we can deal with all these type of imports, unfortunately all the libs included could be written in different style (commonjs, systemjs, webpack, ES6, etc.) , so we have to be flexible when importing them to resolve all the cross-dependencies. It took me hours to import the right packages and to resolve all compiler errors and dependencies but the result was quite satisfying.
Let’s talk about the code a bit. In line 14 I defined a ‘Test group”, a set of tests that are connected logically somehow. In line 16 before any test would run we need to “inject” the previously introduced Service because it is essential for making an instance of the AppComponent class. If we wouldn’t do the service injection, mocha couldn’t resolve the functions defined by the Service’s class. In line 16 before any test would run I created an AppComponent instance by injecting the service to the constructor. The actual tests will use this instance of the AppComponent.
Voila! No more “Couldn’t resolve…” errors!
In line 22 we create our first test called Promise Array. By the nature of promises, it is not guaranteed that the requested data will be available when we run our tests, so I figured I can assert the resolved promise inside a callback method. That is why I created getData function inside app.component.ts to provide the resolved data for our test files. This way we can catch the data and do just what we want to do with it. In my case I asserted if the returning data is an array (yes it is), and if it has a length of 3 (yes it has). Cool.
I should warn you, that callbacks are not the only way to test data provided by a Promise and certainly not the best way, but please note that this is a small project and I have simplicity in mind. I recommend this approach for first-time Unit testers to get the hang of it.
In line 28 I introduce Spies. Spies are powerful tools to intercept function/method behavior. In my example I call the callOnSpy method once and test if it was called just one time (of course it was). The main point here was just to show how to import the chai-spies lib, and how to use it in mocha environment as it wasn’t trivial for me. Please read more from spies here. Finally an image on the successful tests:
There and back again
I do hope that I provided you with all the necessary information to set up your own minimal test environment for Angular2. I am in full knowledge that this isn’t best practice, but I am pretty sure that I would have been delighted and would have saved hours of despair and tears if I had just half of the stuff this article states :) I hope it proves to be useful for some of you, please consume responsibly.
- Angular2: Angular is a development platform (framework) for building mobile and desktop web applications.
- Unit testing: In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use.