Skip to content

Instantly share code, notes, and snippets.

@felippenardi
Last active September 21, 2016 09:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save felippenardi/38949509eed11e6c1527218385579f80 to your computer and use it in GitHub Desktop.
Save felippenardi/38949509eed11e6c1527218385579f80 to your computer and use it in GitHub Desktop.

Testing an Angular $resource factory

This is an example of how to test an Angular Resource created with $resource.

Live Demo

Important take aways:

  • Do not mock $resource. Instead, you can use the resource's methods and assert that it is making the right API call. By doing that you can later change the implementation (for example for replace $resource for Angular Cached Resource) without needing toi go change all previous tests.
  • When intercepting the response of a custom method, do not transform the response string into json yourself. When you use the transformResponse action property of a $resource, you replace the default angular parser that is transforming backend response from string to JSON. It has some logic to ensure that the response is a JSON and can be parsed securely. You can use it by injecting $http into your factory and using $http.defaults.transformResponse.
// demo.module.js
angular.module('demo.module', ['ngResource']);
// demo.resource.js
angular.module('demo.module').factory('User', User);
User.$inject = ['$resource', '$http'];
function User($resource, $http) {
var resource, params, actions, url = null;
activate();
return resource;
function activate() {
url = '/api/users/:id';
params = {
id: '@id'
};
actions = {
query: {
method: 'GET',
isArray: true,
transformResponse:
$http.defaults.transformResponse.concat(queryTransformResponse)
},
get: {
method: 'GET',
transformResponse:
$http.defaults.transformResponse.concat(getTransformResponse)
},
stats: {
method: 'GET',
url: '/api/users/:id/stats'
}
};
resource = $resource(url, params, actions);
}
function buildFullName(data) {
if (data.fullName) { return data; }
data.fullName = data.firstName;
if (data.lastName) { data.fullName += ' ' + data.lastName; }
return data;
}
function getTransformResponse(data) {
data = buildFullName(data);
return data;
}
function queryTransformResponse(data) {
angular.forEach(data, function (user) {
user = buildFullName(user);
return user;
});
return data;
}
}
<!DOCTYPE html>
<html>
<head>
<title>AngularTDD: $resource as factory</title>
<link rel="stylesheet" href="https://jasmine.github.io/2.2/lib/jasmine.css"></link>
</head>
<body>
<script type="text/javascript" src="https://jasmine.github.io/2.2/lib/jasmine.js"></script>
<script type="text/javascript" src="https://jasmine.github.io/2.2/lib/jasmine-html.js"></script>
<script type="text/javascript" src="https://jasmine.github.io/2.2/lib/boot.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.6/angular.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.6/angular-resource.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.6/angular-mocks.js"></script>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="spec.js"></script>
</body>
</html>
describe('Service: User', function() {
var User, _inject, _setup, httpBackend;
User = null;
httpBackend = null;
_inject = function() {
inject(function(_User_, $httpBackend) {
User = _User_;
httpBackend = $httpBackend;
});
};
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
beforeEach(function() {
module('demo.module');
});
describe('the User resource', function() {
beforeEach(function() {
_inject();
});
it('exists', function() {
expect(!!User).toBe(true);
});
it('returns a list of users', function() {
// Arrange
var users;
httpBackend.expectGET('/api/users').respond([{}, {}, {}]);
// Act
users = User.query();
httpBackend.flush();
// Assert
expect(users.length).toBe(3);
});
it('returns stats about a user', function() {
// Arrange
httpBackend.expectGET('/api/users/1/stats').respond({
max: 2
});
// Act
var stats = User.stats({ id: 1 });
httpBackend.flush();
// Assert
expect(stats.max).toBe(2);
});
it('builds the full name when getting a user', function() {
// Arrange
httpBackend.expectGET('/api/users/1').respond({
firstName: "John",
lastName: "Smith"
});
// Act
var user = User.get({ id: 1 });
httpBackend.flush();
// Assert
expect(user.fullName).toBe("John Smith");
});
it('does not rebuilds full name when getting a user', function() {
// Arrange
httpBackend.expectGET('/api/users/1').respond({
fullName: "John Smith"
});
// Act
var user = User.get({ id: 1 });
httpBackend.flush();
// Assert
expect(user.fullName).toBe("John Smith");
});
it('builds the full name when quering users', function() {
// Arrange
httpBackend.expectGET('/api/users').respond([
{
firstName: "John",
lastName: "Smith"
}
]);
// Act
var users = User.query();
httpBackend.flush();
// Assert
expect(users[0].fullName).toBe("John Smith");
});
it('does not rebuilds full name when quering useres', function() {
// Arrange
httpBackend.expectGET('/api/users').respond([
{
fullName: "John Smith"
}
]);
// Act
var users = User.query();
httpBackend.flush();
// Assert
expect(users[0].fullName).toBe("John Smith");
});
it('keeps angular default JSON parser on .query() requests', function() {
// Arrange
var users;
httpBackend.expectGET('/api/users').respond(angular.toJson([
{
firstName: "John"
}
]));
users = User.query();
httpBackend.flush();
expect(users[0].firstName).toBe("John");
});
it('keeps angular default JSON parser on .get() requests', function() {
// Arrange
httpBackend.expectGET('/api/users/1').respond(angular.toJson({
firstName: "John"
}));
// Act
var user = User.get({ id: 1 });
httpBackend.flush();
// Assert
expect(user.firstName).toBe("John");
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment