How to test a new object and its methods in AngularJS service

1,061 views
Skip to first unread message

fpd...@gmail.com

unread,
Aug 14, 2017, 8:00:25 AM8/14/17
to Jasmine
I have a simple service in the angular to retrieve resources from the REST (The code has been trimmed to better illustrate the problem):

function dictionaryResourceService(
ResourceExceptionHandler,
$resource,
$q,
) {
var dictionaryResource = this;

dictionaryResource.insertDictionary = insertDictionary;

function insertDictionary(dictionary) {
var defer = $q.defer();

dictionaryResource.resource.insert(dictionary, function () {
defer.resolve();
}, function (error) {
var resourceException = new ResourceExceptionHandler();

resourceException.setException(error);
resourceException.setExceptionMessage('insert_error');

defer.reject(resourceException);
});

return defer.promise;
}
}

DictionaryResource.service('DictionaryResource', [
'ResourceExceptionHandler',
'$resource',
'$q',
dictionaryResourceService
]);

The ResourceExceptionHandler construct is trivial:

function resourceExceptionHandlerService($q) {
return function () {
var resourceExceptionHandler = this;

resourceExceptionHandler.setException = setException;
resourceExceptionHandler.setExceptionMessage = setExceptionMessage;
};
}

Exception.service('ResourceExceptionHandler', [
'$q',
resourceExceptionHandlerService
]);

Now I wan test case incorrect download data from the server. Everything works fine until a new object is called new ResourceExceptionHandler ()

var dictionaryResource,
dictionaryStub,
$httpBackend;

beforeEach(function () {
module('Exception', 'DictionaryResource');
});

beforeEach(function () {
dictionaryStub = jasmine.createSpyObj('Dictionary', [
'isNew',
'getDictionary'
]);
});

beforeEach(inject(function (_DictionaryResource_, _$httpBackend_) {
dictionaryResource = _DictionaryResource_;
$httpBackend = _$httpBackend_;
}));

afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});

describe('Method: insertDictionary', function () {
it('Should return a ResourceExceptionHandler when Rest server throws error', function () {
var dictionary = dictionaryResource.insertDictionary(dictionaryStub);

expect(dictionary.then).toBeDefined();

$httpBackend.expect('POST', 'api/dictionaries').respond(500, {
reject: true
});
$httpBackend.flush();

expect(dictionaryStub.isNew).toHaveBeenCalled();
expect(dictionaryStub.getDictionary).toHaveBeenCalled();

dictionary.then(null, function (error) {



});
});
});

I'm not able to add a spy to this new ResourceExceptionHandler object. I want to check if setException and setExceptionMessage methods were correctly called. Do you have any idea how to do this?

Gregg Van Hove

unread,
Aug 14, 2017, 12:38:30 PM8/14/17
to jasmi...@googlegroups.com
I would suggest that you make your `ResourceExceptionHandler` not need construction by other parts of your codebase that need it. I don't remember what the angular term for this is though, but you would call a different method than `service` when registering it.

If you can't change that for some reason, you could try spying on the function on the `prototype`. Or you can pass in a fake constructor in your tests that returns a spy object.

Hope this helps. Thanks for using Jasmine!

-Gregg

--
You received this message because you are subscribed to the Google Groups "Jasmine" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jasmine-js+unsubscribe@googlegroups.com.
To post to this group, send email to jasmi...@googlegroups.com.
Visit this group at https://groups.google.com/group/jasmine-js.
For more options, visit https://groups.google.com/d/optout.

fpd...@gmail.com

unread,
Aug 21, 2017, 6:37:26 AM8/21/17
to Jasmine
The constructor is needed because every service in the angular is a singleton. It may happen that I will need several instances of this service at the same time. Option with prototype does not work, I inject ExceptionHndler to test:

beforeEach(inject(function (_DictionaryResource_, _ResourceExceptionHandler_, _$httpBackend_) {
dictionaryResource = _DictionaryResource_;
resourceExceptionHandler = new _ResourceExceptionHandler_;
$httpBackend = _$httpBackend_;
}));

next set spy:

spyOn(resourceExceptionHandler, 'setException');

and check:

dictionary.then(null, function (error) {
expect(resourceExceptionHandler.setException).toHaveBeenCalled();
});

then i have error

Expected spy setException to have been called.


So I add prototype:

spyOn(resourceExceptionHandler.prototype, 'setException');

and I have error

Error: spyOn could not find an object to spy upon for setException()


because resourceExceptionHandler.prototype is undefined.

Gregg Van Hove

unread,
Aug 21, 2017, 8:22:52 PM8/21/17
to jasmi...@googlegroups.com
Check out this stack overflow question about the differences between the different ways to provide things in angular: https://stackoverflow.com/questions/15666048/angularjs-service-vs-provider-vs-factory

You might be able to use a Factory and inject an already constructed thing in your tests, while not needing to use `new` in your real code.

Hope this helps. Thanks for using Jasmine!

-Gregg
Reply all
Reply to author
Forward
0 new messages