How to mock a provider - angularjs

My Angular 1.3 application is using the angular-translate library. In my Karma tests I'm attempting to mock the $translate provider with a Mock object I have created.
The mock object is called MockTranslate and it belongs to the myMocks module. I'm not including the source for MockTranslate in the question as it's not relevant to the question.
The subject of my test is a controller and I can quite easily mock $translate using the following:
module('myMocks');
inject(function($controller, MockTranslate) {
$controller("MyController", {
$translate: MockTranslate.create(translations);
});
});
The above mocking works, however my preference would be to mock the provider using the angular.mock.module with something like:
module('myMocks');
module("myModule", function($provide) {
$provide.provider("$translate", function(MockTranslate) {
return MockTranslate.create(translations);
});
});
But I get the following error when I run my tests:
Error: [$injector:modulerr] Failed to instantiate module function ($provide) due to: Error: [$injector:unpr] Unknown provider: MockTranslate
How do I mock a provider using angular.mock.module?

If I understood the task correctly then here is a working example:
angular.module('translateApp', [])
.controller('translateCtrl', function ($scope, $translate) {
$scope.translate = function(message) {
return $translate.translate(message);
};
})
.provider({
$translate: function() {
this.$get = function () {
return {
translate: function (msg) {
return 'OriginalTranslate: ' + msg;
}
};
};
}
});
describe('Translate Controller Test', function() {
var mockScope;
var mockTranslate;
beforeEach(module('translateApp', function($provide) {
$provide.provider('MockTranslate', function() {
this.$get = function () {
return {
translate: function (msg) {
return 'MockTranslate: ' + msg;
}
};
}
});
$provide.provider('$translate', function() {
this.$get = function (MockTranslate) {
return {
translate: function (msg) {
return MockTranslate.translate(msg);
}
};
}
});
}));
beforeEach(inject(function($controller, $rootScope, $translate) {
mockScope = $rootScope.$new();
mockTranslate = $translate;
$controller('translateCtrl', {
$scope: mockScope,
$translate: mockTranslate
});
}));
it('Translates messages', function () {
expect(mockScope.translate('cool message')).toEqual('MockTranslate: cool message');
});
});

Related

How to Spy on a method of Service from controller?

I am writing the UT for controller and while trying to implement for commandRouter.execute method(please refer 2nd spec) , I am getting error message : can not read property 'execute' of undefined.
Can someone let me know what am i doing wrong here and whats the correct way to spy on a method from controller. ?
module.controller('DcsPlus.AP.OmsControl.omsMasterRecipeDialogPopUpController', omsMasterRecipeDialogPopUpController);
omsMasterRecipeDialogPopUpController.$inject = [
'DcsPlus.Frame.Logic.commandRouter'
];
function omsMasterRecipeDialogPopUpController(commandRouter) {
var vm = this;
vm.execute = function(command) {
commandRouter.execute(command);
};
}
controller.spec.js
describe('omsMasterRecipeDialogPopUpController', function () {
var omsMasterRecipeDialogPopUpControllerTest;
var commandRouterMock;
var $scope;
beforeEach(function () {
registerMockServices();
prepareCommandRouterMock();
});
describe('execute', function () {
it('1. Should check if execute method is defined', function() {
expect(omsMasterRecipeDialogPopUpControllerTest.execute).toBeDefined();
});
it('2. Should check if execute method of commandRouter is called', function() {
omsMasterRecipeDialogPopUpControllerTest.execute();
expect(commandRouterMock.execute).toHaveBeenCalled();
});
});
function prepareCommandRouterMock() {
commandRouterMock = {
execute: function() {
}
};
}
/*beforeEach(function () {
commandRouterMock = jasmine.createSpyObj('DcsPlus.Frame.Logic.commandRouter', ['execute']);
});*/
function registerMockServices() {
angular.mock.module('DcsPlus.AP.OmsControl', function ($provide) {
$provide.value('DcsPlus.Frame.Logic.commandRouter', commandRouterMock);
});
angular.mock.inject(['$controller', '$rootScope', 'dialogService',
function ($controller, $rootScope, dialogService) {
$scope = $rootScope.$new();
spyOn(commandRouterMock, 'execute').and.callThrough();
// Init the controller, passing our spy service instance
omsMasterRecipeDialogPopUpControllerTest = $controller('DcsPlus.AP.OmsControl.omsMasterRecipeDialogPopUpController', {
$scope: $scope
});
}]);
}
});
In the beginning you create commandRouterMock but never assign it to anything.
Try this:
beforeEach(function () {
registerMockServices();
commandRouterMock = prepareCommandRouterMock();
});
function prepareCommandRouterMock() {
return {
execute: function() {
}
};
}

Mocking $mdSideNav in unit test

I have a simple enough function that closes an $mdSidenav instance in my application
function closeSideNav() {
$mdSidenav('left').close();
}
I'm now needing to unit test this, but am having trouble writing an expectation for the close() call on $mdSidenav.
I thought about using $provide in my test spec
module(function($provide) {
$provide.value('$mdSidenav', function(id) {
return {
close: jasmine.createSpy('$mdSidenav.close')
}
})
});
beforeEach(inject(function(_$controller_, _$mdSidenav_) {
$controller = _$controller_;
$mdSidenav = _$mdSidenav_;
}));
beforeEach(function() {
vm = $controller('NavbarController', {
$mdSidenav: $mdSidenav
});
});
describe('vm.closeSideNav', function() {
beforeEach(function() {
spyOn($mdSidenav, 'close');
vm.closeSideNav()
});
it('should call $mdSidenav.close()', function() {
expect($mdSidenav.close).toHaveBeenCalled();
});
});
This throws a couple of errors:
Error: close() method does not exist
Error: Expected a spy, but got undefined.
Has anyone managed to mock out $mdSidenav and offer me some guidance please?
Thanks
UPDATE
Based on the suggested answer, I have now updated my test spec to
'use strict';
describe('NavbarController', function() {
var $controller,
vm,
$mdSidenav,
sideNavCloseMock;
beforeEach(function() {
module('app.layout');
sideNavCloseMock = jasmine.createSpy();
module(function($provide) {
$provide.value('$mdSidenav', function() {
return function(sideNavId) {
return {close: sideNavCloseMock}
}
})
});
});
beforeEach(inject(function(_$controller_, _$mdSidenav_) {
$controller = _$controller_;
$mdSidenav = _$mdSidenav_;
}));
beforeEach(function() {
vm = $controller('NavbarController', {
$mdSidenav: $mdSidenav
});
});
describe('vm.closeSideNav', function() {
beforeEach(function() {
vm.closeSideNav()
});
it('should call $mdSidenav.close()', function() {
expect(sideNavCloseMock).toHaveBeenCalled();
});
});
});
And for a sanity check, my actual controller looks as follows:
(function () {
'use strict';
angular
.module('app.layout')
.controller('NavbarController', Controller);
Controller.$inject = ['$mdSidenav'];
function Controller($mdSidenav) {
var vm = this;
vm.closeSideNav = closeSideNav;
//This only affects the sideNav when its not locked into position, so only on small\medium screens
function closeSideNav() {
$mdSidenav('left').close();
}
}
})();
Unfortunately this still isn't working for me, and I end up with a different error
TypeError: undefined is not a constructor (evaluating '$mdSidenav('left').close())
close method doesn't belong to $mdSidenav. $mdSidenav is a function that returns a side nav object. That's why it complains 'close() method does not exist'.
What you can do is mock the $mdSidenav to return an object hat has mocked close method, like this: -
var sideNavCloseMock;
beforeEach(module(function($provide){
sideNavCloseMock = jasmine.createSpy();
$provide.factory('$mdSidenav', function() {
return function(sideNavId){
return {close: sideNavCloseMock};
};
});
}));
then do
it('should call $mdSidenav.close()', function() {
expect(sideNavCloseMock).toHaveBeenCalled();
});

Unit-Testing a service in Controller with Jasmine in AngularJS

In my Controller I've defined the following service:
CrudService.getAllGroups().$promise.then(
function (response) { $scope.groups = response; },
function (error) { //error code.. }
);
Well, I want to test this service whether it gets a response or not. In test script at first I've defined a function to check whether the service is defined at all.
Test code:
describe('Ctrl: TestCtrl', function () {
beforeEach(module('testApp'));
var scope,
CrudService,
ctrl,
backend;
beforeEach(inject(function ($controller, $rootScope, _CrudService_, $httpBackend) {
scope = $rootScope.$new();
ctrl = $controller('TestCtrl', {
$scope: scope
});
CrudService = _CrudService_;
backend = $httpBackend;
}));
it('should defined the service getGroups', function () {
expect(CrudService.getGroups).toBeDefined();
});
//this is wrong!
it('should returns a successful response', function () {
backend.expectGET('http://localhost:63831/api/group').respond(200, 'success');
backend.flush();
});
});
I don't know how to get a response in the test. I'm new in unit testing and need some help.
For a better comprehension here is the service code:
//CrudService file:
...
return {
getAllGroups: function () {
return ResService.group.query();
}
}
...
//ResService file:
return {
group: $resource(baseUrl + '/api/group/:Id', {
Id: '#Id'
}, {})
}
Do anyone has an idea?
It's incorrect in the sense that it's not a unit test. If you are testing controller here, then you should mock CrudService and test that $scope.groups has been assigned correctly.
beforeEach(function () {
module(function ($provide) {
$provide.factory('CrudService', function () {
return {
getAllGroups: function () {
return {
$promise: null // return an actual promise here
}
}
}
});
});
});
it('should set groups', function () {
expect($scope.groups).toEqual('success')
});
And you need a separate spec to test if CrudService calling backend correctly.

Unit testing controller with injected service

What is the best way to go about unit testing the following controller? I'm having trouble properly injecting AuthService into my controller. I've seen so many different ways to do it and I'm not really sure what the best practice is - i.e. mocks vs spies?
I have a simple service like this:
angular.module('users')
.factory('AuthService', ['$http', '$window',
function($http, $window) {
var authService = {};
authService.login = function(creds) {
return $http.post('/auth', creds)
.then(function(res) {
$window.localStorage.exampleToken = res.data.returned_token;
return res;
});
};
authService.isLoggedIn = function() {
if($window.localStorage.exampleToken) {
return true;
} else {
return false;
}
};
authService.clear = function() {
delete $window.localStorage.exampleToken;
};
return authService;
}]);
My controller:
angular.module('users')
.controller('ExampleCtrl', ['AuthService',
function(AuthService) {
var vm = this;
vm.isLoggedIn = AuthService.isLoggedIn();
}]);
My unfinished test:
describe('ExampleCtrl', function() {
beforeEach(module('users'));
var ctrl;
beforeEach(inject(function($controller) {
ctrl = $controller('ExampleCtrl', {});
}));
describe('when logged in', function() {
beforeEach(function() {
// how do i mock the isLoggedIn function to
// return true
});
it('should return true', function() {
expect(ctrl.isLoggedIn).toBe(true);
});
});
describe('when not logged in', function() {
beforeEach(function() {
// how do i mock the isLoggedIn function to
// return false
});
it('should return false', function() {
expect(ctrl.isLoggedIn).toBe(false);
});
});
});
You can merely use the callFake function of Jasmine:
By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied function.
var AuthService; //so that you can have a reference within all your test file
beforeEach(function() {
inject(function(_AuthService_) {
AuthService = _AuthService_;
});
spyOn(AuthService, 'isLoggedIn').and.callFake(function() {
return true;
});
});

Unit testing promises in AngularJS with arguments

We're unit testing our services and facing issue spying on methods with arguments of dependent services.
I am writing unit tests for ServiceA
ServiceA.js
angular.module("App").service("ServiceA", function($http, ServiceB) {
this.detail = null;
this.method = function(id){
var sevrB = new ServiceB();
return sevrB.getId(1).then(function(response) {
this.detail = response.data;
});
};
});
ServiceB.js (is a factory)
(function () {
var dependencies = [
'../module'
];
define(dependencies, function (module) {
return module.factory('ServiceB', function ($http) {
var ServiceB= function () {
this.id = null;
};
ServiceB.prototype.getId = function(Id) {
return $http.get('/test/');
}
}
}());
Unit test code
describe('Testing ServiceA', function () {
var serviceA, serviceBMock;
beforeEach(function () {
var _serviceBMock = function () {
return {
getId:function(id){
return 'test';
}
};
};
angular.module('ServiceAMocks', [])
.value('ServiceB', _serviceBMock);
});
beforeEach(module('ServiceAMocks'));
beforeEach(inject(function (_ServiceA_, _ServiceB_) {
serviceA=_ServiceA_;
serviceBMock=_ServiceB_;
});
it('retrive Id', function () {
spyOn(serviceBMock,'getId').and.Return('test');
serviceA.method(1);
});
});
I am spying on getId method of ServiceB from ServiceA and if i mocked ServiceB as function i am getting error below
Error: getId() method does not exist
at jasmineInterface.spyOn
If I mock serviceB as object then i get error as
TypeError: object is not a function
var _serviceBMock = {
getId:function(id){
return 'test';
}
}
And I am not sure of testing promise in this scenario.
This version supports Jasmine 1.3
I’m injecting $q service as ServiceB wants to call method then. We can even go forward and resolve returned promise, but this is next step in testing.
Answer to previous version of question, where AngularJS injects instance of serviceB
describe('ServiceA', function () {
var serviceA, ServiceB, $q;
beforeEach(function () {
module('App');
});
beforeEach(function () {
module(function ($provide) {
$provide.value('ServiceB', {
getId: jasmine.createSpy('ServiceB.getId').andCallFake(function () {
return $q.all();
})
});
});
});
beforeEach(inject(function (_ServiceA_, _ServiceB_, _$q_) {
$q = _$q_;
serviceA = _ServiceA_;
ServiceB = _ServiceB_;
}));
describe('.method()', function () {
it('returns ServiceB.getId() argument', function () {
serviceA.method(1);
expect(ServiceB.getId).toHaveBeenCalledWith(1);
});
});
});
jsfiddle: http://jsfiddle.net/krzysztof_safjanowski/sDh35/

Resources