Categories: AngularJS, Software Development, Web Dev Training

AngularJS: testing services that use $resource

In this article we discuss one aspect of AngularJS testing, namely how to unit test code that uses $resource.

When working on a project that uses ngResource (imported into the code as $resource), the unit tests related to the service relying on $resource were failing. I jumped in to do a rewrite or to scrap them entirely if necessary.

  1. The Problem: how to test a service that relies on $resource
  2. Unit Tests Vs. Integration Tests
  3. The Solution: break the dependency and test each part separately

Click here to see the interactive example solution to Testing angularjs services with spies on JSBin

My tool kit for angularjs testing:

  • Karma test runner
  • Jasmine unit testing/spying/mocking framework

The service looked something like this

APRIL 2017 UPDATE: Click here to see the code for testing AngularJS 1.6 services that use $resource.

The Problem

How to unit test a service that relies on $resource was the problem. When I searched around on how to test services or factories that rely on $resource, it appeared that people were trying to mock out $resource which didn’t work. Most of them also tried what we see above and mocked out $httpBackend. This partially works, but did not allow for the forcing of certain code paths which is necessary for getting maximum test coverage.

Unit Tests Vs. Integration Tests

AngularJS uses dependency injection to make it easier to test things like this, but once you’re mocking out objects like $httpBackend and $resource, you’re no longer writing a unit test. You’re instead writing an integration test.

An integration test works by testing the class and its integration with other dependencies. With $resource-based services, your unit test for the service is an integration test.

Great reference books are a god-send and quickly help you solve a problem; they can help you clear your mind and give you a new perspective on a problem

Rudolf Olah on the Book “Working Effectively with Legacy Code”

The Solution: Move Functionality Into a New Class

After some days of searching StackOverflow and other tutorials and articles on how to unit test AngularJS services that use $resource, “Working Effectively with Legacy Code” gave me an answer in a few minutes of reading: move the functionality into a new class.

AngularJS $resource and $httpBackend services
Diagram of the dependency on $resource being broken into two parts (the service and the wrapper that combines the service with $resource).

Working Effectively with Legacy Code: Break the Dependency in the Code

To break the dependency of the service on $resource, I used the advice of “Working Effectively with Legacy Code”. This is a wonderful book that every software developer should have on their shelves or on their ebook reader.

The Importance of Reference Books!

Great reference books are a god-send and quickly help you solve a problem; they can help you clear your mind and give you a new perspective on a problem. I’ve found that to be the case many times with Working Effectively With Legacy Code, especially its key idea that you must break dependencies.

Implementing the Fix

The reason for this is that the callback functions given to the $resource promise are private functions. There is no way of accessing them until you call the promise and force it. This is a common problem, not only in angularjs testing but in other languages (such as PHP) and frameworks (such as Django) as well.

To avoid writing unit tests based on mocking and to avoid having inaccessible/untestable functions, I moved the callback functions to a new class and the OriginalResource now only contains methods that require $resource.

There is now a new service, Original, that doesn’t rely on $resource. The old service, OriginalResource, hooks up the new service to the $resource class. This is far easier to test because you can invoke the success or failure callbacks whenever you like and check to make sure it changes the right parts of the system.

Let me go over those steps again:

  1. Notice that it is hard to test OriginalService which uses $resource directly
  2. Create a new class, OriginalResourceService that depends on $resource
  3. Move code that relies on $resource from OriginalService into OriginalResourceService (this is known as a sprout class, a way of breaking dependencies)
  4. You can now write a unit test for OriginalService that ensures it delegates to OriginalResourceService and that handles both the “happy path” and the error cases such as special exceptions or error codes
  5. You can now write a unit test for OriginalResourceService with $resource mocks to handle the happy path and error cases such as $resource being returned a 404 error

Conlusion: Break Dependencies with Single-Responsibility Classes

The conclusion: when you’re writing services that interact with the backend, you may need to break your service into two services to avoid mock objects. And this is okay! There’s nothing wrong with refactoring code to make it testable without having to resort to mocks. “Working Effectively with Legacy Code” has a lot of other good advice for dealing with spaghetti code.

Click here for Angular 2 vs 1 complete list of differences

Click here to find other articles on AngularJS and to subscribe to the Learning AngularJS newsletter

The full source code for the example in this angularjs testing article is in a Gist on Github:

If you’re an Emacs user, be sure to check out the angularjs-mode for Emacs.

Check out some books on AngularJS.

Rudolf Olah is a software development expert with over 8 years of professional software developer experience. He has produced the video courses "Reactive Programming in Python with RxPy, PyQt5 and Tornado" and "Learning AngularJS Testing" for PacktPublishing. Rudolf offers web development training courses for individual developers and for web development teams. He writes about tech leadership, career coaching and project management.