Unit Testing a Simple Service in AngularJs

Monday, July 14, 2014

Introduction

When testing in AngularJs, I have found that once you have everything setup correctly, unit testing with Jasmine is not all that complicated. However, when I first started testing in AngularJs, getting things setup right was a bit hit or miss for me. At the time, the documentation for testing wasn't all that definitive so it took me a little longer to grasp the concepts than probably should have been needed.

So in this post, I am going to show how I test a simple service that takes an array of directory paths as a string and parses it out into an array of arrays. Its a simple test for a service that has no dependencies and only one function.

I am basically building on my previous post where I showed how to setup a unit testing environment using NodeJs, GruntJs, KarmaJs, and Jasmine.


The Service

So in this example, I am taking an AngularJs factory that returns one function. This function should take an array of paths and convert them into an array of array of directories.

The whole point of encapsulating this code is that not only is it testable, but is also mockable, and it is reusable.


The Unit Test

So here is the test:

The way I setup a unit test for the module, is I divide the test module into three different sections.

  1. The first section I include any variables that will be used by all the tests. This includes things like the scope, mocks, services, controllers etc. On a side note, if my mock data becomes too big, I separate them out into a different file and place the file in a folder called mocks and then reference them from there. My Karma.conf.js file then will need a reference to this folder.
  2. The next section is the most critical section and that is the section that sets everything up before each test runs.
  3. The last section is the section that handles all the tests.

Let me focus on the second section as that for me has always been the trickiest to get right.

BeforeEach

Jasmine comes with two global variables (beforeEach and afterEach), and as the names describe, the beforeEach runs before each test, while the afterEach runs after each test.

Here I am only using the beforeEach to set up the tests.

Module

The module command is used to specify what AngularJs module you are going to test. You need not worry about any other dependent modules, at this point, the module routine is only specific to the module you currently want to test, even if you have other dependent modules.

Inject

The inject function wraps objects into an injectable function and is where I am initializing the service. In this case I am only referencing my service that I want to test since I don't have any other dependencies.

Notice the underscores in the parameters.These underscores are actually ignored by the injector but allow us differentiate between what is being injected and the module level variable that we want to use for all out tests so they can be assigned accordingly.

The Actual Tests

Finally, the last part of the file contains the actual tests themselves.

If I am using a TDD approach, the first thing I do is make sure the service exists. This is represented by the test, "should be defined as a service". Once this test passes, I am then ready to move on to my next test.

I then test to make sure the method I am actually testing in the service exists. This test is represented by the test, "should have a function called getReportList". Again, once this test passes, I can then move to me next tests.

Then my last two tests, test that the function is actually returning what I am expecting it to return.

Conclusion

Hope that helps. In later posts I will show some different unit tests, such as testing a controller, testing a service that has a $HttpBackend dependency, and testing a controller that has a service dependency. In those posts I hope to cover things like mocks, spies, and the $HttpBackend service as well. 

comments powered by Disqus