Unit testing becomes a challenging task when one is not used to doing it. I faced a similar scenario writing my first application in Angular JS. I took more 3x more time to write unit tests than writing the actual controllers, services etc. I was not sure what and how to unit test. In this post I will go through of what I think is a comprehensive unit test for a controller in Angular JS and for that I will be using Jasmine as my testing framework.
** One thing I believe everyone should concentrate on is that it is a “unit” test and everything else this “unit” is dependent upon must be mocked out. (e.g. services, APIs, route resolves etc)
Controllers in Angular JS are only responsible to interacting with the view via the scope and almost any other operation is done via the Angular Services. So, the controllers in my application (and probably yours too) will contain:
- POJOs on the scope which are provided as attributes to the directives used in the view
- Functions which make makes use of an angular service for data manipulation or making API calls or any other operation. They might return promises, so a controller can have success and error hooks
So a typical controller would look like this:
Lets go ahead and analyze what unit tests would completely test this controller …
I often do a little exercise to visualize the controller as a tree and chalk out all the possible unit tests. For example, unit tests for the above controller would be something like this…
Once our analysis of unit tests is complete we will then set up the controller’s test file and it looks like this …
** Setting up the test environment is out of the scope of this document
In the above code:
- we mocked out our route resolve and injected it into our controller instance i.e. adminListResolveMock
- we created a Jasmine Spy object with all the methods we need i.e. adminsServMock
- finally we added some return values to ShowDeleteConfirmModal and GetAdminList so that we can resolve and reject these promises on will to test the code completely
let’s go ahead and start writing unit tests for each node in the above tree from left to right..
Testing initialize values and types
In the following unit tests, I just made sure that all the initialize values and needed functions are present in the controller.
Testing onDeleteAdmin function
The following unit tests, will test the onDeleteAdmin function with all its possibilities.
*** Note, since we are resolving and rejecting promises outside the Angular cycle, we need to let Angular know when we resolved or rejected a promise. It is accomplished via scope.$apply() method.
Testing onViewPortals function
The following unit tests will test the onViewPortals function with all its possibilities
With this, we complete unit testing our sample controller. You can find the source for the controller and its unit tests on the following links:
Controller is here
Unit test is here
Controllers if written with the aim of just to interact with the view are pretty simple to test. Apart from how to test controllers, I believe it is important to create either a mind-map or a small diagram which outlines all the possibilities for the tests. This technique applies to all tests, irrespective of language or framework and helps one stay focused. I recommend that the readers should at least give it a try.
If you have any questions, comments please feel free to ask.