AngularJS : unit tests and end to end tests

This entry was posted by on Vendredi, 19 octobre, 2012 at

If there is one feature compared to other JS framework that make AngularJS better, it is the ability to implement tests. This is normal I would say since the framework was developed by integrating this fundamental aspect at the beginning. JavaScript is a dynamically typed language which makes it easy to write but allows writing errors that the compiler will not see. It is therefore really important when you use JavaScript to write tests.
But AngularJS is not magic either, as anyone who has implemented tests know, it is important to think its code for testing. You have to be careful not using objects or methods without knowing whether they will be easily testable. For example, a controller, if in one of the calls are made there is DOM access via jQuery for example, this code immediately become complex to test.
Unfortunately, current tools do not allow us to make TDD as easily than with Java, when I writing a test the IDE will propose the creation of classes and methods. However, nothing prevents it.
AngularJS don’t imposes a testing framework and you can play with your tools, but the team has developed AngularJS libraries to facilitate tests writing and they are based on Jasmine and JsTestDriver (Misko is a co-author):

Let’s go to practice.

Manually Running Jasmine + JsTestDriver

With angular-seed, launch scripts are provided to implement the tests. Just run scripts/test-server.[sh|bat] to start JsTestDriver and open a page on each browser you want to test at http://localhost:9876/. Then run the tests manually : scripts/test.[sh|bat] . All tests reported in test/unit will be executed. You can change this behavior by targeting specific test or other directories via the configuration file config/jsTesDriver.conf

Run in IDE: Eclipse and IntelliJ / WebStorm

JsTestDriver provided plugins for Eclipse and IntelliJ. So you can run your tests in your favorite IDE rather than the command line. Be careful though if you are using angular-seed to review the paths declared in config/jsTesDriver.conf that do not run in an IDE.

Test a controller

Start with the following code : a simple list of names with a delete button and followed by a text field with a button to add new name:

We must test the controller to ensure that the addPatient() method adds even a patient in the list. Though mind you the use of module(‘myApp’) that will load the corresponding module (look like Guice) as well as the use of inject($rootScope, $controller). inject(…) allows much argument you want, these arguments must refer to objects known to the module and are identified by their name: injected(MyService) will not work if you don’t have declared MyService. Similarly inject($controleur) will not work because the service is called $controller.

Test a directive

Using ui-date directive from our previous article and detail it. As above, module() and inject() are used to inject our dependencies. Though our mind that jQuery-UI datepicker is design for testing because you can interact with the component via methods that simulate user actions. This is an important point for choosing an external library that you want to encapsulate in a directive.

Test user interface or end to end testing

With E2E we can simulate user interactions with the GUI. I hear some said “why not Selenium ?”, well some have tried
but it is still more complex. More Selenium does not know the operation of AngularJS and trials have shown problems with delays. Misko summarized in the forum why they have not used Selenium.
On the other hand the use of jQuery selectors tags to target is very simple to write.
The example below shows how, from the previous code, try adding a name in the list.

To run this test in angular-seed it is necessary to modify test/e2e/scenario.js. Then you must get your site started and then just call the URL http://localhost:port/test/e2e/runner.html in your browser. This gives the following report:

Simulation an AJAX call

To test server calls AngularJS provided a mock service : $http (discussed in more detail in a future article). And tests can be run independently of the server. Go back to our previous example and use the resource data/patients.json to retrieve the names of patients:

where data/patients.json contains

To perform the test it is necessary to inject the object $httpBackend. Then it will intercept $http service calls to return then data. In the following test method when() will indicate that the resource should return data/patients.json will be:

$httpBackend provided two methods:

  • when(): will define a global response
  • expect(): will define precise response for a specific resource

Also note the flush() method, server requests are asynchronous and this should not be the case in the execution of tests. The flush() method forces the query to run and simulate a synchronous mode.

Testacular

To run the tests in real time (like infinitest) on different browsers Vojta has developed Testacular: engine running under node.js integrated into IntelliJ / WebStorm. The log trace can even show the error stack and make reference to the test. IntelliJ / WebStorm makes it clickable and allows you to go directly to the relevant file.
I let you discover this handy tool through this video.

  • Mohammad Islam

    I wrote a pretty easy to use angular seed with a cleaner directory layout and modules loaded asynchronously with RequireJS. The most important part is, it loads controllers in a per-demand basis, meaning: there is no overhead in loading bunch of unnecessary controllers before you need. Definitely, give it a shot.