Is there any way to spy on isMyResponse
(function() {
'use strict';
angular
.module('mymodule')
.factory('MyInterceptor', MyInterceptor);
MyInterceptor.$inject = [...];
function MyInterceptor(...) {
var self = this;
self.isMyResponse = isMyResponse;
return {
request: {},
response: function (response) {
if(self.isMyResponse(response)){
}
}
};
function isMyResponse(response){
...
}
}
})();
Your code is not structured particularly well to enable testing. You would be much better off to add isMyResponse to the MyInterceptor prototype. This way, you could spy on the the prototype in your tests:
function MyInterceptor(...) { ... }
MyInterceptor.prototype.isMyResponse = function (...) {
// the original isMyResponse method goes here
};
Then in the tests, you should be able to do something like this:
beforeEach(inject(MyInterceptor => {
spyOn(MyInterceptor, 'isMyResponse');
});
Related
After a week looking for a good answer/sample, I decided to post my question.
I need to know how is the best way to code and test something like this:
Controller
// my.controller.js
(function () {
'use strict';
angular.module('myApp.myModule').controller('Awesome', Awesome);
function Awesome($http, $state, AwesomeService) {
var vm = this; // using 'controllerAs' style
vm.init = init;
vm.awesomeThingToDo = awesomeThingToDo;
vm.init();
function awesomeThingToDo() {
AwesomeService.awesomeThingToDo().then(function (data) {
vm.awesomeMessage = data.awesomeMessage;
});
}
function init() {
vm.awesomeThingToDo(); // Should be ready on page start
}
}
})();
Service
// my.service.js
(function () {
'use strict';
angular.module('myApp.myModule').factory('AwesomeService', AwesomeService);
function AwesomeService($resource, $http) {
var service = {
awesomeThingToDo: awesomeThingToDo
}
return service;
function awesomeThingToDo() {
var promise = $http.get("/my-backend/api/awesome").then(function (response) {
return response.data;
});
return promise;
}
}
})();
My app works OK with this structure. And my Service unit tests are OK too.
But I don't know how to do unit tests on Controller.
I tried something like this:
Specs
// my.controller.spec.js
(function () {
'use strict';
describe("Awesome Controller Tests", function() {
beforeEach(module('myApp.myModule'));
var vm, awesomeServiceMock;
beforeEach(function () {
awesomeServiceMock = { Is this a good (or the best) way to mock the service?
awesomeThingToDo: function() {
return {
then: function() {}
}
}
};
});
beforeEach(inject(function ($controller) {
vm = $controller('Awesome', {AwesomeService : awesomeServiceMock});
}));
it("Should return an awesome message", function () {
// I don't know another way do to it... :(
spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function() {
return {
then: function() {
vm.awesomeMessage = 'It is awesome!'; // <-- I think I shouldn't do this.
}
}
});
vm.awesomeThingToDo(); // Call to real controller method which should call the mock service method.
expect(vm.awesomeMessage).toEqual('It is awesome!'); // It works. But ONLY because I wrote the vm.awesomeMessage above.
});
});
})();
My app uses Angular 1.2.28 and Jasmine 2.1.3 (with Grunt and Karma).
UPDATE: Solved!
it("Should return an awesome message", function () {
// Solved with callback parameter
spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) {
return {
then: function(callback) {
callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D
}
}
});
I updated the question with a possible (bad) solution:
it("Should return an awesome message", function () {
// Solved with callback parameter
spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) {
return {
then: function(callback) {
callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D
}
}
});
I used a callback to pass the mocked parameter and call the real implementation. :D
No, that's not how I would do this.
First, there is no need to create a mock service: you can inject the real one, and spy on it.
Second, Angular has everything you need to create promises and to resolve them. No need to create fake objects with a fake then() function.
Here's how I would do it:
describe("Awesome Controller Tests", function() {
beforeEach(module('myApp.myModule'));
var vm, awesomeService, $q, $rootScope;
beforeEach(inject(function($controller, _awesomeService_, _$q_, _$rootScope_) {
$q = _$q_;
awesomeService = _awesomeService_;
$rootScope = _$rootScope_;
vm = $controller('Awesome');
}));
it("Should return an awesome message", function () {
spyOn(awesomeService, "awesomeThingToDo").and.returnValue(
$q.when({
awesomeMessage: 'awesome message'
}));
vm.awesomeThingToDo();
// at this time, the then() callback hasn't been called yet:
// it's called at the next digest loop, that we will trigger
$rootScope.$apply();
// now the then() callback should have been called and initialized
// the message in the controller with the message of the promise
// returned by the service
expect(vm.awesomeMessage).toBe('awesome message');
});
});
Unrelated note: 1.2.28 is quite old. You should migrate to the latest version.
I want to develop a generic translator component with configurable url and paramsFn. Here paramsFn can either be a plain function or a function with service dependencies. paramsFn is expected to return a promise.
(function () {
"use strict";
angular.module("translator-app", [])
.provider(translatorProvider);
function translatorProvider() {
var
url,
paramsFn;
//Provider Config Functions
function setUrl (pUrl) {
url = pUrl
};
function setParamsFn (pParamsFn) {
paramsFn = pParamsFn;
};
function factory ($http, $q) {
//Service Function Pseudo
function translate(key) {
if (translateions are cached) {
//return promis of cached translations
} else {
/*
make http call with configured url and
paramsFnto fetch translations.
Cache translations.
Return promise with translations.
*/
}
} //translate
//Service Object
return {
translate: translate
};
} // factory
factory .$inject = [
"$http"
"$q"
];
//Exposed functionality
this.setUrl = setUrl;
this.setParamsFn = setParamsFn;
this.$get = factory;
}
}();
An application can use translator after configuring it. User app provide will be able to provide paramFn with service dependencies. paramFn will be invoked later when translator.translate(...) method is called.
(function () {
"use strict";
angular.module('the-app', ["translator-app"])
.config(translatorConfigurator)
.controller(AppController)
function translatorConfigurator (translatorProvider) {
function theParamsFn (someService) {
//use someService to generate and return params object
}
theParamsFn.$inject = [
"someService"
];
translatorProvider.setUrl("/url/to/translator");
translatorProvider.setParamsFn(theParamsFn);
}
function AppController (translator) {
translator.translate("the-key").then(function (translated) {
//do somethid with 'translated'.
});
}
translatorConfigurator.$injec = [
"translatorProvider"
];
AppController.$inject = [
"translator"
];
}());
How can I achieve this?
Short Story:
According to Angular $injector documentation
// inferred (only works if code not minified/obfuscated)
$injector.invoke(function(serviceA){});
// annotated
function explicit(serviceA) {};
explicit.$inject = ['serviceA'];
$injector.invoke(explicit);
// inline
$injector.invoke(['serviceA', function(serviceA){}]);
Novel
Once upon a time there was a poor translatorProvider. Angular, a great super hero, helped translatorProvider to be feature rich by its $injector weapon. translatorProvider built its getParameters function inside factory function and used it in translate.
(function () {
"use strict";
angular.module("translator-app", [])
.provider(translatorProvider);
function translatorProvider() {
var
url,
paramsFn;
//Provider Config Functions
function setUrl (pUrl) {
url = pUrl
};
function setParamsFn (pParamsFn) {
paramsFn = pParamsFn;
};
function factory ($injector, $http, $q) {
function getParameters() {
var
promise,
fn;
if (paramsFn) {
fn = $injector.invoke(paramsFn);
promise = $q.resolve(fn());
} else {
promise = $q.resolve()
}
return promise;
}
//Service Function Pseudo
function translate(key) {
if (translateions are cached) {
//return promis of cached translations
} else {
getParameters()
.then(function (params) {
return $http({
url: url,
params: params
});
})
.then(function (response) {
var extracted = ...; //extract field from response.data
//put extracted into cache
return $q.resolve(extractedField)
});
}
} //translate
//Service Object
return {
translate: translate
};
} // factory
factory .$inject = [
"$injector",
"$http"
"$q"
];
//Exposed functionality
this.setUrl = setUrl;
this.setParamsFn = setParamsFn;
this.$get = factory;
}
}();
Now translator can be configured as below.
(function () {
"use strict";
angular.module('the-app', ["translator-app"])
.config(translatorConfigurator)
.controller(AppController)
function translatorConfigurator (translatorProvider) {
function theParamsFn (someService) {
return function () {
//returns some parameters object
}
}
theParamsFn.$inject = [
"someService"
];
translatorProvider.setUrl("/url/to/translator");
translatorProvider.setParamsFn(theParamsFn);
}
function AppController (translator) {
translator.translate("the-key").then(function (translated) {
//do somethid with 'translated'.
});
}
translatorConfigurator.$inject = [
"translatorProvider"
];
AppController.$inject = [
"translator"
];
}());
After these changes translatorprovider becomes more powerful and help many other modules and they lived happily ever after.
How to create Jasmine unit test for one function in AngularJS service provider. I want to create mock data for myObject and test function getObjectShape() with that mock data as parameter. How to achieve that?
(function () {
'use strict';
angular.module('objectShapes')
.provider('shapesResolver', shapesResolver);
function shapesResolver() {
this.$get = function () {
return resolver;
};
function resolver(myObject) {
var service = {
getObjectShape: getObjectShape
};
function getObjectShape() {
return myObject.Shape;
}
}
}
})();
Here's a skeleton of a test for your service.
describe('shapesResolver service', function() {
var shapesResolver;
beforeEach(module('objectShapes'));
beforeEach(inject(function(_shapesResolver_) {
shapesResolver = _shapesResolver_;
}));
it('should do something, but what?', function() {
var mockMyObject = {};
shapesResolver(mockMyObject);
// shapesResolver doesn't return anything, and doesn't
// have any side effect, so there's nothing to test.
expect(true).toBeTruthy();
});
});
I have a controller function like this:
$scope.localTimezone = function (userTimezone,datetime) {
// ....
return data;
}
What is the correct way to make it a factory module? I tried the following but it's giving errors.
angular.module('localTimezone', [])
.factory('localTimezone',function(userTimezone,datetime) {
// ...
return data;
});
angular.module('app', ['localTimezone'])
.controller('tasksController',function ($scope,localTimezone) {
// ...
});
I am missing out on some concept or logic.Can anyone please point me in the right direction?
CONTROLLER Example
Bad:
function MainCtrl () {
this.doSomething = function () {
};
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
Good:
function MainCtrl (SomeService) {
this.doSomething = SomeService.doSomething;
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
Factory Example
Bad:
function AnotherService () {
var someValue = '';
var someMethod = function () {
};
return {
someValue: someValue,
someMethod: someMethod
};
}
angular
.module('app')
.factory('AnotherService', AnotherService);
Good:
function AnotherService () {
var AnotherService = {};
AnotherService.someValue = '';
AnotherService.someMethod = function () {
};
return AnotherService;
}
angular
.module('app')
.factory('AnotherService', AnotherService);
For Detail Guidelines go through this blog :
Opinionated AngularJS styleguide for teams
Here is a working code example based on the assumption userTimezone and datetime are services which are apart of the localTimezone module.
The following has been modified
'data' which your factory is returning has been modified to return a string based on the factory pattern - as you were returning 'data' which referenced nothing
constructing the app has been moved to the top. This code should execute before anything else.
app variable removed - I don't like global variables.
Code:
angular.module('app', ['localTimezone']);
angular.module('localTimezone', []).factory('localTimezone',
function(userTimezone, datetime) {
var data = 'hello';
return { data: data } ;
});
angular.module('localTimezone').service('userTimezone', function() {
});
angular.module('localTimezone').service('datetime', function() {
});
angular.module('app').controller('tasksController',function ($scope,localTimezone) {
});
Codepen: http://codepen.io/anon/pen/wijmb (no errors appearing in console)
Take a look at http://angular-tips.com/blog/2013/08/understanding-service-types for information about the different service types in Angular.
I have created an Angular factory that has methods which handle saving code to a server. One of the factory methods contains a third party object which has a method which does the actual callout. I would like to test this code, but I can't work out how to mock out the third party object.
I have set up a plunker with a Jasmine test.
My aim for this test is just to successfully get the code to use my mock object rather than the ThirdPartySavingUtils object. Is that possible?
var app = angular.module("MyApp", []);
app.factory("SavingUtils", function() {
return {
saveStuff: function() {
if(typeof ThirdPartySavingUtils !== "undefined") {
return ThirdPartySavingUtils.value;
}
}
};
});
this is my jasmine tests
describe("Mocking Test", function() {
var ThirdPartySavingUtilsMock;
var SavingUtils;
beforeEach(function() {
angular.mock.module("MyApp", function($provide) {
ThirdPartySavingUtilsMock = {
value: "I am the mock object"
};
$provide.value("ThirdPartySavingUtils", ThirdPartySavingUtilsMock);
});
inject(function(_SavingUtils_) {
SavingUtils = _SavingUtils_;
});
});
it("should run without throwing an exception", function() {
expect(true).toBe(true);
});
it("should mock out ThirdPartySavingUtils with ThirdPartySavingUtilsMock", function() {
var result = SavingUtils.saveStuff();
expect(result).toEqual("I am the mock object");
});
});
You have a few options really but more than likely you would need to do both.
1) You could create an angular service which wraps this third party object - this way you get a nice abstraction incase you ever need to change the third party object.
2) You could use a mocking framework like http://sinonjs.org/ which enable you to mock methods out and do asserts like calledOnce etc.
Here is a link to a mocked test using sinon test.
You can bascially see sinon is used as a sandbox to mock out an object methods. Sinon provides extra propeties to those mocked methods so you can assert if they were called, the parameters they were called with even the order of the calls. It is a really, really essential testing tool.
describe('validationManager', function () {
beforeEach(inject(function ($injector) {
sandbox = sinon.sandbox.create();
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
$q = $injector.get('$q');
defer = $q.defer();
validator = $injector.get('validator');
validationManager = $injector.get('validationManager');
sandbox.stub(validator, 'makeValid');
sandbox.stub(validator, 'makeInvalid');
sandbox.stub(validator, 'getErrorMessage').returns(defer.promise);
setModelCtrl();
}));
afterEach(function () {
sandbox.restore();
setModelCtrl();
});
it('should be defined', function () {
expect(validationManager).to.exist;
});
describe('validateElement', function () {
it('should return if no $parsers or $formatters on the controller', function () {
validationManager.validateElement(modelCtrl);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(false);
});
});
EDIT -----------------------
Here this put into practice for your code (I haven't run this but it give the general idea).
(function (angular, ThirdPartyApi) {
'use strict';
var app = angular.module('MyApp', []);
app.factory('thirdPartApi', [
function () {
return {
save: ThirdPartyApi.save,
value: ThirdPartyApi.value
};
}
]);
app.factory('SavingUtils', [
'thirdPartApi',
function (thirdPartApi) {
var getValue = function () {
return thirdPartApi.value;
},
save = function (item) {
return thirdPartApi.save(item);
};
return {
save: save,
getValue: getValue
};
}
]);
}(angular, window.ThirdPartyApi));
The tests.....
(function (angular, sinon) {
'use strict';
describe('MyApp.SavingUtils', function () {
var sandbox, thirdPartyApi, SavingUtils, thirdPartyApiValue = 2;
beforeEach(inject(function ($injector) {
sandbox = sinon.sandbox.create();
thirdPartyApi = $injector.get('thirdPartyApi');
SavingUtils = $injector.get('SavingUtils');
// stub the method and when called return a simple object or whatever you want
sandbox.stub(thirdPartyApi, 'save').returns({ id: 1});
sandbox.stub(thirdPartyApi, 'value', function () {
return thirdPartyApiValue;
});
}));
afterEach(function () {
// This removes those stubs and replace the original methods/values
sandbox.restore();
});
describe('save', function () {
it('should return call the save method on thirdPartyApi', function () {
var item = {};
SavingUtils.save(item);
expect(thirdPartyApi.save.calledOnce).to.equal(true);
});
});
describe('getValue', function () {
it('should return value of value property on thirdPartyApi', function () {
var result = SavingUtils.getValue();
expect(result).to.equal(thirdPartyApiValue);
});
});
});
}(angular, sinon));