Unknown Provider when unit testing filter - angularjs

I am unit testing my first filter, which seems easy enough but I keep getting
Unknown provider: activeProfileProvider <- activeProfile <- pbRoles <- rolesFilter
The activeProfile is a consent that is a dependency of pbRoles. My understanding of constants is limited and I cant seem to find a way to include it. I have tried adding the config file where the constants are declared as well as mocking the constant in the test to no avail.
does anyone know if there is a specific way to do this? or does my problem lay else where?
My filter:
angular.module('pb.roles.filters')
.filter('roles', ['pbRoles', function (pbRoles) {
return function (input) {
if (!input) {
return "None";
} else {
return pbRoles.roleDisplayName(input);
}
};
}]);
My test:
describe('RolesController', function () {
beforeEach(module('pb.roles'));
beforeEach(module('pb.roles.filters'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var rolesFilter;
var mockActiveProfile = {};
beforeEach(inject(function (_rolesFilter_) {
activeProfile = mockActiveProfile;
rolesFilter = _rolesFilter_;
}));
var pbRoles = {
roleDisplayName: function (input) {
return input
}
};
describe('role: filter', function () {
it('should return None if called with no input', function () {
expect(rolesFilter()).toBe('None');
});
it('should call roleDisplayName with input', function () {
expect(roles('Hello')).toBe('Hello');
});
});
});
have also tried mocking the constent like so:
module(function ($provide) {
$provide.constant('activeProfile', function () {
pbGlobal.activeProfile = {
profile: ""
}
});
});

mocking the providers at the top of the page like so worked:
beforeEach(module(function ($provide) {
$provide.constant('organizationService', function () {
});
$provide.service('activeProfile', function () {
activeProfile = {
profile: ""
}
});
}));
http://www.sitepoint.com/mocking-dependencies-angularjs-tests/ was a huge help.
My full working test if anyone is curious:
describe('RolesController', function () {
var mockPbRoles = {
roleDisplayName: function (input) {
return "it worked!"
}
};
beforeEach(module(function ($provide) {
$provide.value('pbRoles', mockPbRoles);
$provide.constant('organizationService', function () {});
$provide.service('activeProfile', function () { });
}));
beforeEach(module('pb.roles'));
beforeEach(module('pb.roles.filters'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var rolesFilter;
beforeEach(inject(function (_rolesFilter_) {
rolesFilter = _rolesFilter_;
}));
describe('role: filter', function () {
it('should return None if called with no input', function () {
expect(rolesFilter(false)).toBe('None');
});
it('should call roleDisplayName with input', function () {
result = rolesFilter(true);
expect(result).toEqual("it worked!");
});
});
});

Related

$uibModal.open is not a function in karma (AngularJS Factory)

I am trying to test a function that opens a $uibmodal. This is my factory function.
confirmationMessage: function (message) {
var modalInstance = $uibModal.open({
templateUrl: 'views/templates/utilsTemplates/confirmationMessage.html',
backdrop: 'static',
controller: function () {
var messageCtrlVM = this;
// message to show
messageCtrlVM.message = message;
// when yes_button is pressed
messageCtrlVM.yesPress = function () {
modalInstance.close(true);
};
// when no_button is pressed
messageCtrlVM.noPress = function () {
modalInstance.close();
};
},
controllerAs: "messageCtrlVM"
});
return modalInstance.result;
},
In its unit testing file, I am first adding provider for it like so.
beforeEach(angular.mock.module('ui.bootstrap'));
beforeEach(function () {
module(function ($provide) {
$provide.value('$uibModal', function (value) {
return value;
});
});
});
After that I am injecting open, dismiss and close functions with beforeEach like so.
beforeEach(inject(function (_utilsFactory_, _$httpBackend_, _$filter_) {
utilsService = _utilsFactory_;
$httpBackend = _$httpBackend_;
filter = _$filter_;
uibModal = {
open: function () {},
dismiss: function () {},
close: function () {}
};
}));
Finally I am trying to run my unit test by calling factory function.
it('should show a confirmation message', function () {
var spy = spyOn(uibModal, "open").and.callFake(function () {
return {
result: {
then: function () {}
}
};
});
utilsService.confirmationMessage("Are you Sure?");
expect(spy).toHaveBeenCalled();
});
It gives me error that $uibModal.open is not a function.
Your beforeEach should be like this:
beforeEach(module('myApp', function ($provide) {
mockModal = {
result: {
then: function(confirmCallback, cancelCallback) {
this.confirmCallback = confirmCallback;
this.cancelCallback = cancelCallback;
return this;
}
},
opened: {
then: function (confirmCallback, cancelCallback) {
this.confirmCallback = confirmCallback;
this.cancelCallback = cancelCallback;
return this;
}
},
close: function() {
this.opened.confirmCallback(); // covers opened.then success
this.result.confirmCallback(); // covers result.then success
this.result.cancelCallback(); // covers result.then error
},
open: function (object1) {
return this;
}
};
$provide.value('$uibModal', mockModal);
}));
Note that, here, the object which we are providing as $uibModal has the open function. Having this passed in $provide, you would need to callThrough (not callFake after spying)
Feel free to remove result / opened / close if you are not using them here. They are useful when you have corresponding code.

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- $state.includes error is blocking other tests /how to mock state

I am trying to unit test the init function of this controller.I cannot find a way of doing this and keep getting the error
TypeError: 'undefined' is not a function (evaluating '$state.includes('profile.details')')
My main concern is to mock the state so that this error goes away and my other tests can pass and then I will focus on writing the test for the init statement. I have tried mocking the state as a string and using transitionTo, has anyone else found a way of testing includes for states?
var init = function () {
$scope.global = global;
$scope.partialViews = {
personForm: "/app/users/views/details/_personForm.html",
passwordForm: "/app/users/views/details/_passwordForm.html"
};
if (!$state.includes('profile.details') && !$state.includes('profile.organizations')) {
if (global.activeProfile.defaultOrganizationId) {
$state.go("dashboard.notifications", { orgId: global.activeProfile.defaultOrganizationId });
} else {
$state.go("profile.organizations");
}
}
};
As an aside I went back to reading the docs to see how the .includes method works in hopes that it would help me figure out what I am doing wrong in my unit tests. in the MDN docs the .includes example is [1, 2, 3].includes(2); but typing this into the console responded with a
Uncaught TypeError: undefined is not a function
why is that?
my tests:
beforeEach(module('pb.users.controllers'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var mockUserService = {};
var mockOrganizationService = {};
var mockPersonInvitationService = {};
var mockStateParams = {};
var mockState = "profile.details";
var mockGlobal = {};
var mockForm = {};
var mockModal = {};
var invitation = {};
beforeEach(inject(function ($q) {
invitation = {
organizationId: 6542643
};
mockForm = {
submitIfValid: function (promiseHandler) {
return promiseHandler();
}
};
mockStateParams = {
accountId: 7672891,
entityId: 532,
orgId: 67,
page: 43,
length: 12
};
mockGlobal = {
setFormSubmitInProgress: function (boolean) {
this.formProgress = boolean;
},
formProgress: false,
activeOrganizationId: 432,
organizationsUpdated: function () {
return "updated!"
}
};
mockUserService = {
user: {
person: {
name: 'Regan Perkins'
}
},
getUser: function () {
var defer = $q.defer();
defer.resolve(this.user);
return defer.promise;
},
updateExtendedInfo: function (person) {
var defer = $q.defer();
defer.resolve(this.user);
return defer.promise;
}
};
mockOrganizationService = {
organizations: {
groups: ["PressBoard", "MySite"]
},
getOrganizations: function () {
var defer = $q.defer();
defer.resolve(this.organizations);
return defer.promise;
}
};
mockPersonInvitationService = {
invitations: ["invite one", "invite two"],
getInvitations: function () {
var defer = $q.defer();
defer.resolve(this.invitations);
return defer.promise;
},
acceptInvitation: function (organizationId) {
var defer = $q.defer();
defer.resolve(invitation);
return defer.promise;
}
};
}));
beforeEach(inject(function ($rootScope, _$controller_) {
scope = $rootScope.$new();
$controller = _$controller_;
controller = $controller('ProfilesController', {
$scope: scope,
$stateParams: mockStateParams,
$state: mockState,
$modal: mockModal,
global: mockGlobal,
userService: mockUserService,
organizationService: mockOrganizationService,
personInvitationService: mockPersonInvitationService
});
}));
describe('init() function', function () {
it('should set activeOrganizationId', function () {
expect(scope.global.activeOrganizationId).toEqual(mockGlobal.activeOrganizationId);
});
it('should set global', function () {
expect(scope.global).toEqual(mockGlobal);
});
});
describe('get() function', function () {
it('should resolve a promise', function () {
scope.get();
scope.$digest();
expect(scope.person).toEqual(mockUserService.user.person);
});
});
describe("edit() function", function () {
it("should toggle personFormSuccess", function () {
spyOn(mockUserService, "updateExtendedInfo").and.callThrough();
scope.edit(mockForm, mockUserService.user);
expect(mockUserService.updateExtendedInfo).toHaveBeenCalledWith(mockUserService.user);
});
it("should call updateExtendedInfo()", function () {
spyOn(mockUserService, "updateExtendedInfo").and.callThrough();
scope.edit(mockForm, mockUserService.user);
expect(scope.personFormSuccess).toBe(false);
scope.$digest();
expect(scope.personFormSuccess).toBe(true);
});
});
describe('getOrganizations() function', function () {
it('should resolve a promise', function () {
scope.getOrganizations();
scope.$digest();
expect(scope.organizations).toEqual(mockOrganizationService.organizations);
});
});
describe('getInvitations() function', function () {
it('should resolve a promise', function () {
scope.getInvitations();
scope.$digest();
expect(scope.invitations).toEqual(mockPersonInvitationService.invitations);
});
});
describe('acceptInvitation() function', function () {
it('should toggle form progress', function () {
scope.invitations = mockPersonInvitationService.invitations;
scope.acceptInvitation(invitation, 1);
scope.$digest();
expect(scope.invitations).toEqual(mockPersonInvitationService.invitations);
});
it('should resolve a promise', function () {
scope.invitations = mockPersonInvitationService.invitations;
scope.acceptInvitation(invitation, 1);
scope.$digest();
expect(scope.invitations).toEqual(mockPersonInvitationService.invitations);
});
it('should resolve a promise', function () {
scope.invitations = mockPersonInvitationService.invitations;
scope.acceptInvitation(invitation, 1);
scope.$digest();
expect(scope.invitations).toEqual(['invite one']);
});
});
describe("openRejectInvitation() function", function () {
var actualOptions;
var modalOptions = {
templateUrl: '/app/users/views/organizations/_removeInvite.html',
controller: 'RejectInvitationModalController',
resolve: {
invitation: function () {
return invitation;
}
}
};
beforeEach(inject(function ($injector, $q) {
mockModal.open = function (options) {
actualOptions = options;
var defer = $q.defer();
defer.resolve();
return { result: defer.promise };
}
}));
it("make sure modalInstance.result.then is executed", function () {
scope.invitations = mockPersonInvitationService.invitations;
scope.openRejectInvitation(invitation, 1);
expect(scope.invitations).toEqual(['invite one', 'invite two']);
scope.$digest();
expect(scope.invitations).toEqual(['invite one']);
});
it("make sure modal.open is called", function () {
spyOn(mockModal, 'open').and.callThrough();
scope.openRejectInvitation(invitation, 1);
expect(mockModal.open).toHaveBeenCalledWith(actualOptions);
});
it("make sure 'webSite' is passed by modalInstance.resolve", function () {
scope.openRejectInvitation(invitation, 1);
expect(actualOptions.resolve.invitation()).toEqual(invitation);
});
});
});
Incase this helps anyone else with this problem the way I was able to bypass this error was to mock the includes method on my mockState.
var mockState = {
includes: function (string) {
return false
}
};

How to mock an object within an AngularJS factory method

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));

How can I inject $window when testing a custom AngularJS provider?

I need to unit test a custom provider overriding the $windowProvider.
provider.js
angular
.module('customProvider', [])
.provider('cprovider', [
'$windowProvider',
function ($windowProvider) {
var $window = $windowProvider.$get();
var platform = function () {
// some code there use $window
};
this.platform = platform;
this.$get = function () {
return {
platform: platform
};
};
}
]);
cprovider.spec.js
describe('cprovider', function () {
var cprovider, mockWindow;
describe("xxxx", function () {
beforeEach(function () {
mockWindow = {navigator: {userAgent: 'xxxx'}};
module('customProvider', function ($provide) {
$provide.value('$window', 'mockWindow');
});
inject(function (_cprovider_) {
cprovider = _cprovider_;
});
});
it('should something', function () {
// Arrange and Act in beforeEach.
// Assert. WON'T WORK
expect(cprovider.platform()).toBe('xxx');
});
});
});
Can't mock properly the $windowProvider.
Anyone knows how can I do that?
You can spyOn $window:
beforeEach(function () {
angular.mock.module('customProvider');
inject(function (_$window_, _cprovider_) {
cprovider = _cprovider_;
$window = _$window_;
spyOn($window, 'alert');
});
});
Complete fiddle here

Resources