Unit testing Angular directive that consumes services - angularjs

I have a directive that consumes a service:
angular.module('app', [])
.service('myService', function() {
return {
getCustomer: function(){
return {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}
};
})
.directive('myCustomer', function(myService) {
return {
link: function(scope){
scope.customer = myService.getCustomer();
},
template: 'Name: {{customer.name}} Address: {{customer.address}}'
};
});
I'm trying to unit test this, but I can't seem to figure out how to inject the service into my directive in the unit test.
var tests;
(function (tests) {
describe('myCustomer Directive', function () {
var scope, createDirective;
beforeEach(angular.mock.module('app'));
beforeEach(angular.mock.module('templates'));
beforeEach(angular.mock.inject(function ($injector) {
var $compile = $injector.get('$compile');
var myService = $injector.get('myService');
scope = $injector.get('$rootScope');
// where do I inject myService???
createDirective = function () {
return $compile('<my-customer></my-customer>')(scope);
};
}));
describe('on creation', function () {
var sut;
beforeEach(function () {
sut = createDirective();
scope.$digest();
});
it('creates an element node', function () {
var contents = sut.contents();
expect(contents[0].nodeType).toBe(sut[0].ELEMENT_NODE);
});
});
});
})(tests || (tests = {}));
The problem is that I need to be able to explicitly inject the dependency so I can mock some of it's calls. Is this possible?
Here's a Plunker with my app code.

So turns out, as JB Nizet pointed out, that all you need to do is spy on the service and it all takes care of itself.
beforeEach(angular.mock.inject(function ($injector) {
var $compile = $injector.get('$compile');
scope = $injector.get('$rootScope');
var myService = $injector.get('myService');
// set up spies
spyOn(myService, "getCustomer").and.returnValue({foo: 'bar'});
createDirective = function () {
return $compile('<my-customer></my-customer>')(scope);
};
}));

Related

Controller Knows About Service Mock, but Unit Test Doesn't (AngularJS, Karma-Jasmine)

I'm adding unit tests to my AngularJS application, and running into an issue where the controller I'm testing is aware of my mocks (logs the correct mock object), but my unit tests cannot return the mock value. I've been stuck on this one for a while.
Mock Service
angular.module('mocks.myService', [])
.factory('myService', function() {
var service = {
hi : 'hi'
};
return service;
});
Controller
.controller('MyCtrl', ['myService', function(myService) {
var vm = this;
vm.doThing = function() {
console.log(myService);
// Object{hi:'hi'}
};
}]);
Unit Test
describe('myApp.selection module', function() {
var myCtrl, myService;
beforeEach(function() {
myService = module('mocks.myService');
console.log(myService);
// undefined
});
describe('controller', function(){
it('should exist', inject(function($controller) {
myCtrl = $controller('MyCtrl');
expect(myCtrl).toBeDefined();
}));
it ('should do thing', function() {
myCtrl.doThing();
});
});
});
Try this:
describe('myApp.selection module', function () {
var myCtrl, myService;
beforeEach(module("mocks.myService"));
beforeEach(inject(function (_myService_) {
myService = _myService_;
console.log(myService);
}));
describe('controller', function () {
it('should exist', inject(function ($controller) {
myCtrl = $controller('MyCtrl');
expect(myCtrl).toBeDefined();
}));
it('should do thing', function () {
myCtrl.doThing();
});
});
});
source: https://docs.angularjs.org/guide/unit-testing

Passing one of the parameters in constructor to unit test

I am trying to get/set "testModel.number" in my unit test but I can't seem to get it. When I run the test I get this error message:
Error: [$injector:unpr] Unknown provider: testModelProvider <- testModel
Here is the controller:
angular.module("TestApp", [])
.controller("IndexController", function ($scope, testModel) {
$scope.name = "test";
testModel = {
number: 0
}
if (testModel.number === 1) {
$scope.name = "test1";
} else {
$scope.name = "test2";
}
});
Here is the unit test:
describe('IndexController', function () {
var scope, createController;
beforeEach(module("TestApp"));
beforeEach(inject(function ($rootScope, $controller, testModel) {
scope = $rootScope.$new();
createController = function () {
return $controller('IndexController', {
'$scope': scope,
'testModel': testModel
})
}
}));
it('example test', function () {
var controller = createController();
testModel.number = 1;
expect(scope.name).toBe('test1');
});
});
I'm fairly new to unit testing so any suggestions would be great! Thanks in advance!
I think you need to pass the testModel object to the method creating the controller. Jasmine does not know how to inject custom providers.
describe('IndexController', function () {
var scope, createController;
beforeEach(module("TestApp"));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
createController = function (testModel) {
return $controller('IndexController', {
'$scope': scope,
'testModel': testModel
})
}
}));
it('example test', function () {
var testModel = { number: 1 };
var controller = createController(testModel);
expect(scope.name).toBe('test1');
});
});
If you will have multiple tests that will need the testModel object, you can also define it at a global level as follows:
describe('IndexController', function () {
var scope, createController;
var testModel = { number: 1 };
beforeEach(module("TestApp"));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
createController = function () {
return $controller('IndexController', {
'$scope': scope,
'testModel': testModel
})
}
}));
it('example test', function () {
var controller = createController();
expect(scope.name).toBe('test1');
});
});

Angular $scope method testing

I'm trying to test if a $scope method has been called
my controller :
(function () {
var app = angular.module('productsController',[]);
app.controller('AddProductController', ['$scope',function ($scope) {
$scope.test = function(){
return true;
};
$scope.test();
}]);
})();
and my test :
describe("Products Controller", function () {
beforeEach(function () {
module('productsController');
});
beforeEach(function () {
var createController, scope, rootScope;
});
describe('AddProductController', function () {
beforeEach(function () {
inject(function ($controller, _$rootScope_) {
rootScope = _$rootScope_;
scope = _$rootScope_.$new();
createController = function () {
return $controller("AddProductController", {
$scope: scope,
});
};
});
});
it('should call test', function(){
spyOn(scope, 'test');
createController();
scope.$apply()
expect(scope.test).toHaveBeenCalled();
})
});
});
but I got this error:
Error: test() method does not exist
$scope.test exists any when I run the app in a browser the method is been called, but if falis whne testing, any clues?
May be there is some problem in the ordering, Could you try and check if the below works?
describe('AddProductController', function() {
var createController, scope, rootScope;
beforeEach(function() {
inject(function($controller, _$rootScope_) {
rootScope = _$rootScope_;
scope = _$rootScope_.$new();
createController = function() {
return $controller("AddProductController", {
$scope: scope,
});
};
});
});
it('should call test', function() {
createController(); // To create function test in the scope
spyOn(scope, 'test');
createController(); // To execute test after spy is added.
expect(scope.test).toHaveBeenCalled();
})
});

How to restrict to invoke service dependencies while writing Unit test for Controller?

I have the following controller:
(function () {
"use strict";
angular.module('usp.configuration').controller('ConfigurationController', ConfigurationController);
ConfigurationController.$inject = ['$scope', '$rootScope', '$routeParams', 'configurationService'];
function ConfigurationController($scope, $rootScope, $routeParams, configurationService) {
//Get Master Gas List
configurationService.getMasterGasList().then(function (response) {
$scope.masterGasList = response.data.data;
});
$scope.convertToInt = function (str) {
if (!isNumberEmpty(str) && !isNaN(str)) {
return parseInt(str, 10);
}
return "";
}
$scope.convertToString = function (num) {
if (!isNumberEmpty(num) && !isNaN(num)) {
return num + "";
}
return "";
}
}
}());
And below is the test case for the controller:
describe("test suite for Configuration test controller", function() {
var scope = null;
var configurationService;
beforeEach(module("usp.configuration"));
beforeEach(inject(function($rootScope, $controller, _configurationService_) {
// Services
// _'s are automatically unwrapped
configurationService = _configurationService_;
// Controller Setup
scope = $rootScope.$new();
$controller("ConfigurationController", {
$scope: scope,
configurationService : configurationService
});
}));
it("should convert to int", function() {
expect(scope.convertToInt("2")).toEqual(2);
});
it("should return empty string", function() {
expect(scope.convertToInt("asd")).toEqual("");
});
});
I don't want to call that service while I am running the test case.
I am new to unit testing, I don't know how can I do this.
Please help me to do this?
You need to mock the dependencies with $provide
beforeEach(function () {
configurationServiceMock = {
getSomething: function () {
return 'mockReturnValue';
}
};
module(function ($provide) {
$provide.value('configurationService', configurationServiceMock);
});
});
see: Injecting a mock into an AngularJS service
Solution for your needs:
var configurationServiceMock = {
getMasterGasList: function () {
return {
then: function(callback) {}
};
}
};
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('ConfigurationController', {
'$scope': scope,
'configurationService': configurationServiceMock
});
}));

Testing for return value from $q.when in Angular JS using jasmine

I have a factory as follows:
var myFactory = function ($q) {
factory = {};
factory.newObject = function() {
return $q.when({ id: 0 }});
};
};
angular.module('myModule').factory('myFactory', myFactory);
How can I use jasmine to test the newObject function and ensure id returned is 0?
Here's what I have so far:
describe("My Factory Test", function () {
var testMyFactory;
beforeEach(module('myModule'));
beforeEach(inject(function (myFactory) {
testMyFactory = myFactory;
}));
it('New Entity', function ($q) {
testMyFactory.newObject().then(function(myObj) {
expect(myObj.id).toBe(0); // does not work
});
});
});
this worked:
describe("My Factory Test", function () {
var testMyFactory, rootScope;
beforeEach(module('myModule'));
beforeEach(inject(function (myFactory, $rootScope) {
testMyFactory = myFactory;
rootScope = $rootScope;
}));
it('New Entity', function () {
var testObj;
testMyFactory.newObject().then(function(myObj) {
testObj = myObj;
});
rootScope.$apply();
expect(testObj.id).toBe(0);
});
});

Resources