Testing in Angular World

 

This has been a hot topic for me lately. I covered testing AngularJS in couple of blogs but over the time I noticed people who are coming from .Net world who are not used to TDD model still fall back to the old style development that is without tests or tests have been added after the fact. I think, either of the approach is totally wrong. Testing is first class citizens in AngularJS and we should treat them that way. I want to go over couple of argument I got when I asked them about why there are no tests or not following TDD and lets discuss about it. I will try to answer the best of ability.

We are still learning AngularJS so I can’t write unit tests.

Ok it seems very valid point. I would strongly recommend understand and learn how to do tests first. Testing in Angular is simple and straight forward. I am going to focus on testing in the coming blogs. Angular support both unit testing and end to end testing (e2e) testing. It is easy to learn and get going fast. First understand AngularJS concepts like $scope, $http and others then learn how to mock them, the rest is nothing but writing simple tests. The bottom line, if you are trying to learn AngularJS, learn testing along with basic concepts and then go into the details of AngularJS features.

Now going back point made, if you want to do something and you do not know how to do it in AngularJS then you can not write test for it. Well that is not 100% true. If you are following TDD model, then once you get the requirement that you are trying write code for, you are suppose to write the test first. Now that you already know how to write test, go ahead and write the test first and then try to do the implementation. When you are trying to implement, you may run into problems, then modify the tests as you make progress to fit the model you are writing. This is true in end to end testing as well (e2e), you might have end to end test against a form and as you learn you realize that you need data table, change the e2e test for it. But write the test first. It will help. Again, with the tests, even when you are learning, it catch the unexpected errors that you might make when experimenting with new concepts.

We can’t write tests because there is no logic in the code.

Excellent point, AngularJS is used to develop client side code. So if you are developing application properly, client side should be as dumb as possible. So in theory, we will have MVC pattern applied to both client side and server side. So all the complex business logic will be in the server side controller. Since you do not have complicated logic at client side, client side code should be very simple. Normally when we start out the client side code will be very simple, we might make a webapi call to get some data, bind them to the variables and that is it. So for that we do not need any tests. Trust me when I say this, that is how all the projects start. Always they are very simple in the beginning and as project start to grow, you will have more complex logic in it and at that time, it will be difficult to add test to it.

In the above scenario, my suggestion is to add unit tests to all the functions you might add to client side code, even if it is single line assignment statement. You will also agree, it is easy to write test cases of single line assignment statement, so go ahead and add the tests for it. It might seem silly, but now when you run the test, you have code coverage for the functions you have written. The benefit comes in the next iteration you are asked to add some logic to that method with simple assignment. You already have tests so you know why that function exists to start with and with that understanding you can add more tests to support new functionality without breaking the old ones. In this case, you are trying to understand what that function suppose to do since you might not have written the code, so you do not know what it was suppose to do. With tests, it is easy to see the scenarios and understand what exactly those methods suppose to do. It gives the confidence in your change you are making.

AngularJS provides not only unit testing, it also gives e2e testing. So even if there are no logic in the client side, you are working with the data coming from server to display data. So you can write tests for it. Write your e2e test with mock data and verify if the UI elements display the data as it suppose to be. The cases where the developer thinks there is no logic in the client side, then you will have more e2e tests than unit tests.

Angular JS – 9 (End to End Testing – manual)

Continuing from the previous blog, lets look at end to end testing. Based on what you have seen so far, it is very obvious without telling, how easy it to test Angular JS. The true separation of view and controller lend itself to test controller very easily. Everything is injectable which makes everything mockable in Angular JS. Now that we know, we can test controller with ease with Jasmine and Karma. Lets look at the other cool testing we can do in Angular, which is End To End test.

Angular End to End test allows the developer to do UI testing. This allows developer to enter values directly in the DOM elements and perform operations and then validate the results. This have been a difficult task so far with other client technologies and Angular JS made it easy for us. These end to end tests are called scenario tests. Angular follows Jasmine model of writing tests for end to end test as well.

If you have not already downloaded the Intro to Karma from Github, please download and we will use the same code to go over the end to end test.

There are two ways one could run end to end test. The first approach is to create your test and run the test manually and verify if the tests are passing. The second approach is to have the test run automatically in the background all the time while you developing your application and writing the tests. With this approach there is no need to run the test every time one would make a change to the code or tests. We will review the first approach in this blog.

Manual Test:

First lets look at the test we are going to write. Our application is very simple, it takes two numbers, multiplies and show the result on the screen as shown below

image

Since we are doing the UI testing, lets see the view under test.

1 <table> 2 <tr><td>First Number</td><td><input type="text" ng-model='firstNumber'/></td></tr> 3 <tr><td>Second Number</td><td><input type="text" ng-model='secondNumber'/></td> 4 <td><button ng-click='Multiply(firstNumber, secondNumber)'>Multiply</button></td> 5 ><td><input type="text" ng-model='decimals'/></td></tr> 6 <tr><td>Result</td><td>{{result | number: decimals}}</td></tr> 7 </table>

The two input fields are bound to ‘firstNumber’ and ‘secondNumber’, so in our test, we need to enter values for those two fields. Then find the button and click the button. Which would then perform the multiplication and save the result to ‘result’. So to confirm our UI code worked properly, we need to verify if ‘result’ contains the expected result. With this knowledge lets look at the test. The end to end test is available under test\e2e\mainscenario.js

As I mentioned before, end to end test also borrowed testing pattern from jasmine where we describe what we are testing and before each test and after each test, one could perform some task. All tests where marked by ‘it’. So going back to the test we were going to do;

1 it('should multiply two numbers', function(){ 2 input('firstNumber').enter(2); 3 input('secondNumber').enter(2); 4 element(':button').click(); 5 expect(binding('result')).toEqual('4.00'); 6 });

Line 1: starts the test with ‘it’, followed by two parameters, first one is the description of the test, followed by the test itself.

Line 2: Find the input for ‘firstNumber’ and enter value ‘2’.

Line 3: Find the input for ‘secondNumber’ and enter value ‘2’.

Line 4: Find the only button and click the button to perform multiplication.

Line 5: Find the ‘result’ and verify the result is ‘4.00’. Even though when we multiply 2 * 2 is 4, by default the decimal digit is set to 2 and result ‘4’ is send to number filter so the result becomes ‘4.00’.

When we do end to end test, we are testing the html page, so before each test, we need to make sure, the browser is pointing to the page under test. To do that, one need to define the beforeEach or at the start of each test. In our case, we define a beforeEach to route to the index.html as shown below

1 beforeEach(function(){ 2 browser().navigateTo('../../app/index.html'); 3 });

Line 2: has ‘../../app/index.html’. This is very important. To run, end to end test, one need to run the a web server first. Since I will be using node and the web server script is at test/script, it is very important you run the web server first from this location as follows

node test\scripts\web-server.js

now if you follow the line (2), what we are doing is, trying to find index.html from app folder two directories up from web-server.js location. That’s why we have the navigation. If web-server.js placed in root directory, then our navigateto would be ‘app/index.html’.

With node server is running, we need to create a html file to run our end to end script. It will be very simple script, we need to point to angular-scenario.js script and also our end to end script that’s all about it. So here is our runner.html file

1 <!doctype html> 2 <!-- to run e2e make sure you are running your web server --> 3 <!-- in our example we run node test/scripts/web-server .js --> 4 <!-- also open browser and point to http://localhost:8000/test/runner.html --> 5 <!-- we need to make sure we run node same way when we run the e2e test in command line as well --> 6 <html lang="en"> 7 <head> 8 <title>End2end Test Runner</title> 9 <script src="lib/angular-scenario.js" ng-autotest></script> 10 <script src="e2e/mainscenario.js"></script> 11 </head> 12 <body> 13 </body> 14 </html>

Please read step 2-5 in the html file to make sure everything is setup.

Now launch Chrome and point to the url in line 4 and that is it. You will have your end to end test run.

image

If you get the above screen then you are all set. In case if you run into problem, then you might not have one or more of the following not setup properly as mentioned in ng-model website. Also please try with Chrome do not try with IE.