Mock a $stage.go transition - angularjs

I have a controller with an activate() method:
function CustomerController(customerFactory, $state, $stateParams) {
var vm = this;
vm.getCustomer = getCustomer;
activate();
function activate() {
if($state.is('customer-view')) vm.getCustomer($stateParams.id);
}
function getCustomer(id) {
return customerFactory.getCustomer(id)
.then(getCustomerComplete)
.catch(getCustomerFailed);
function getCustomerComplete(response) {
return vm.customer = response;
}
function getCustomerFailed(error) {
return vm.error = error;
}
}
}
I want to test, using jasmine, that on $state.go('customer-view', {id: 1}) the getCustomer(id) function has been called.
Here is my test:
it("should be called", inject(function($state) {
spyOn(vm, 'getCustomer');
$state.go('customer-view', {id: 1});
expect(vm.getCustomer).toHaveBeenCalledTimes(1);
}));
And the test returns "Expected spy getCustomer to have been called once. It was called 0 times."
PS: the code above is working, I just don't know how to test it.
EDIT:
Here a bigger example of my spec file:
describe("app.customers controller", function () {
var $controller, customers, deferred, customerFactoryMock, vm, $rootScope, $q;
beforeEach(function () {
module('app.customers');
});
beforeEach(inject(function (_$rootScope_, _$controller_, _$q_, _customerFactory_) {
customerFactoryMock = _customerFactory_;
$controller = _$controller_;
vm = $controller("CustomerController", {customerFactory: customerFactoryMock});
$q = _$q_;
deferred = $q.defer();
$rootScope = _$rootScope_;
customers = [{"id": 1}, {"id": 2}];
}));
describe("getCustomer(id)", function () {
it("should call the factory method", function () {
spyOn(customerFactoryMock, 'getCustomer').and.returnValue(deferred.promise);
vm.getCustomer(1);
expect(customerFactoryMock.getCustomer).toHaveBeenCalledTimes(1);
});
it("should be called", inject(function($state) {
spyOn(vm, 'getCustomer');
$state.go('customer-view', {id: 1});
expect(vm.getCustomer).toHaveBeenCalledTimes(1);
}));
it("should have a customer on resolved", function () {
spyOn(customerFactoryMock, 'getCustomer').and.returnValue(deferred.promise);
deferred.resolve({"id": 1});
vm.getCustomer(1);
$rootScope.$digest();
expect(vm.customer).toBeDefined();
expect(vm.customer.id).toEqual(1);
});
it("should return an error message on reject", function () {
spyOn(customerFactoryMock, 'getCustomer').and.returnValue(deferred.promise);
deferred.reject('this is a reason');
vm.getCustomer();
$rootScope.$digest();
expect(vm.error).toBeDefined();
expect(vm.error).toBe('this is a reason');
});
});
});

Related

Angular karma testing controller

I'm trying to figure out these karma tests to test my Angular app. I can figure out simple things. But I am having problems with one that connects to my database.
I am wanting to test the $scope.getMultipleOptions function in the controller below:
$scope.options = [];
$scope.getMultipleOptions("form_field_1", $scope.options);
$scope.getMultipleOptions = function (key, opts) {
var id = key.replace("form_field_", "");
var promise = DynamicFormFactory.GetData('/DynamicForm/GetMultipleOptions?form_field_id=" + id);
promise.then(
function (success) {
angular.forEach(success, function (o) {
opts.push(o);
});
},
function (error) {
// Error
}
);
}
This is what my factory/service looks like:
dynamicFormApp.factory('DynamicFormFactory', ['$http', function ($http) {
return {
GetData: function (url) {
return $http.get(url)
.then(function (response) {
return response.data;
}, function (error) {
return error;
});
}
}]);
And this is what I've done to set up my karma test
describe('DynamicFormController', function () {
beforeEach(module('dynamicFormApp'));
var $controller;
var $rootScope;
beforeEach(inject(function (_$controller_, _$rootScope_) {
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
describe('$scope.getMultipleOptions', function () {
var $scope, controller;
beforeEach(function () {
$scope = $rootScope.$new();
controller = $controller('DynamicFormController', { $scope: $scope });
$scope.fields = [];
});
it('$scope.getMultipleOptions', function () {
var key = "form_field_15";
var expectedResult = [{ desc: "DESCRIPTION", id: "ID" }];
$scope.getMultipleOptions(key, $scope.fields);
expect($scope.fields).toEqual(expectedResult);
});
});
});
The test always fails as $scope.fields is always empty. How do I do this?

Testing John Papa's controller calls to dataservice

I am learning BDD with Jasmine and Karma.
I'm following John Papa's style guide and I have some difficulties testing my code.
I want to test this kind of controller function (from John Papa's guide):
function getAvengers() {
return dataservice.getAvengers()
.then(function(data) {
vm.avengers = data;
return vm.avengers;
});
}
With this factory:
function dataservice($http) {
return {
getAvengers: getAvengers
};
function getAvengers() {
return $http.get('/api/maa')
.then(getAvengersComplete)
.catch(getAvengersFailed);
function getAvengersComplete(response) {
return response.data.results;
}
function getAvengersFailed(error) {
// Fail
}
}
}
I've made these test:
describe("avengers controller", function() {
var controller, avengers;
beforeEach(function() {
module('app');
});
beforeEach(inject(function(_$rootScope_, _$controller_, _$q_, _dataservice_) {
dataserviceMock = _dataservice_;
controller = _$controller_;
vm = controller("AvengerController", { dataservice: dataserviceMock });
$q = _$q_;
rootScope = _$rootScope_;
avengers = [{"id": 1}, {"id": 2}];
}));
it('should be created successfully', function () {
expect(vm).toBeDefined();
});
it('should have called `dataservice.getAvengers`', function() {
spyOn(dataserviceMock, 'getAvengers').and.callFake(function(){
var deferred = $q.defer();
return deferred.promise;
});
vm.getAvengers();
expect(dataserviceMock.getAvengers).toHaveBeenCalledTimes(1);
});
// What I'm trying to test
// Test 1
it('should have Avengers', function() {
spyOn(dataserviceMock, 'getAvengers').and.callFake(function(){
var deferred = $q.defer();
deferred.resolve([{"id": 1}, {"id": 2}]);
return deferred.promise;
);
vm.getAvengers();
expect(vm.avengers.length).toEqual(avengers.length);
});
// Test 2
it('should have Avengers', function() {
spyOn(dataserviceMock, 'getAvengers').and.returnValue(avengers);
vm.getAvengers();
expect(vm.avengers.length).toEqual(avengers.length);
});
});
I tried two different ways but it didn't work and I can't figure out how to test that avengerController.getAvengers() defines vm.avengers equal to the returned data avengers from the factory call.
I found a solution:
it("should have avengers", function() {
deferred = $q.defer();
spyOn(dataserviceMock, 'getAvengers').and.returnValue(deferred.promise);
deferred.resolve(customers);
vm.getAvengers();
expect(dataserviceMock.getAvengers).toHaveBeenCalled();
scope.$digest();
expect(vm.avengers).toBeDefined();
expect(vm.avengers).toEqual(avengers);
});
I had to return a promise and use the scope.$digest() to simulate the scope life cycle as mentionned here.

can't get promise result from angular service

I'm trying to mock a service I'm using and should return a promise, the mock service is being called but I can't get the result to my test.
service function to be tested:
function getDevices() {
console.log('getDevices');
return servicesUtils.doGetByDefaultTimeInterval('devices')
.then(getDevicesComplete);
function getDevicesComplete(data) {
console.log('getDevicesComplete');
var devices = data.data.result;
return devices;
}
}
My test is:
describe('devicesService tests', function () {
var devicesService;
var servicesUtils, $q, $rootScope;
beforeEach(function () {
servicesUtils = {};
module('app.core', function ($provide) {
servicesUtils = specHelper.mockServiceUtils($provide, $q, $rootScope);
});
inject(function (_devicesService_, _$q_, _$rootScope_) {
devicesService = _devicesService_;
$q = _$q_;
$rootScope = _$rootScope_.$new();
});
});
it('getting device list', function () {
console.log('getting device list');
devicesService.getDevices().then(function (result) {
console.log(result);
expect(result).toBeDefined();
});
});
});
Mock file:
function mockServiceUtils($provide, $q) {
var servicesUtils = {};
servicesUtils.doGetByDefaultTimeInterval = jasmine.createSpy().and.callFake(function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
$rootScope.$digest();
return deferred.promise;
});
$provide.value('servicesUtils', servicesUtils);
return servicesUtils;
}
Your code is way too complex.
Let's assume that you want to test a service devicesService that uses another service servicesUtils, having a method that returns a promise.
Let's assume devicesService's responsibility is to call servicesUtils and transform its result.
Here's how I would do it:
describe('devicesService', function() {
var devicesService, servicesUtils;
beforeEach(module('app.core'));
beforeEach(inject(function(_devicesService_, _servicesUtils_) {
devicesService = _devicesService_;
servicesUtils = _servicesUtils_;
}));
it('should get devices', inject(function($q, $rootScope) {
spyOn(servicesUtils, 'doGetByDefaultTimeInterval').and.returnValue($q.when('Remote call result'));
var actualResult;
devicesService.getDevices().then(function(result) {
actualResult = result;
});
$rootScope.$apply();
expect(actualResult).toEqual('The transformed Remote call result');
}));
});

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

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

Resources