AngularJS Jasmine - Test with different Mock Response in next 'it' block - javascript

describe('test mock', function () {
var resource, employeeRoles, provider, mockBaseUrl, mockUser, mockOffices, mockCalendar, $window, $httpBackend, $scope, $serializer;
beforeEach(angular.mock.module('appointmentManager'));
beforeEach(inject(function ($injector, $rootScope, $httpParamSerializer) {
mockCalendar = {
// sample data
};
$window = $injector.get('$window');
$window.ApiBaseUrl = mockBaseUrl;
$window.LoggedInUser = mockUser;
$httpBackend = $injector.get('$httpBackend');
$httpBackend.expectPOST('api/Calendar/GetCalendar').respond(200, mockCalendar);
$scope.$apply();
$httpBackend.flush();
}));
it('should be defined in module', function () {
expect(resource).toBeDefined();
expect(provider).toBeDefined();
});
it('active employees should be true', function () {
expect(provider.isNoActiveEmployeesAvailable).toBe(false);
//test with another mockCalendar data
});
afterEach(function () {
$scope.$destroy();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
});
I want to test with different Mock Response in Next it block. Here, how can I assign new value in mockCalendar variable before the second it block runs?

In your scenario, which is not uncommon, I typically create a function that has code that is going to be common in all my tests, and then call it from your test, after additional arrangements that cannot be performed in the beforeEach. Note this is different than using the beforeEach, because you still need to be able to modify something prior to calling the function.
For instance, your code would be modified to be like the following:
describe('test mock', function () {
var resource, employeeRoles, provider, mockBaseUrl, mockUser, mockOffices, mockCalendar, $window, $httpBackend, $scope, $serializer;
var setupTest = function(mockCalendar) {
$httpBackend.expectPOST('api/Calendar/GetCalendar').respond(200, mockCalendar);
$scope.$apply();
$httpBackend.flush();
};
beforeEach(angular.mock.module('appointmentManager'));
beforeEach(inject(function ($injector, $rootScope, $httpParamSerializer) {
mockCalendar = {
// sample data
};
$window = $injector.get('$window');
$window.ApiBaseUrl = mockBaseUrl;
$window.LoggedInUser = mockUser;
$httpBackend = $injector.get('$httpBackend');
}));
it('should be defined in module', function () {
setupTest(mockCalendar); // not sure if this is needed here.
expect(resource).toBeDefined();
expect(provider).toBeDefined();
});
it('should set provider.isNoActiveEmployeesAvailable to false when xxx', function () {
mockCalendar.someField = 'some value';
setupTest(mockCalendar);
expect(provider.isNoActiveEmployeesAvailable).toBe(false);
});
it('should do something else', function () {
mockCalendar.someField = 'some other value';
setupTest(mockCalendar);
expect(provider.isNoActiveEmployeesAvailable).toBe(true);
});
afterEach(function () {
$scope.$destroy();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
});

Related

Unit test inject dependency like controller for service in angularJS

Lets see, we have this according to:https://docs.angularjs.org/guide/unit-testing
describe('PasswordController', function() {
beforeEach(module('app'));
var $controller;
beforeEach(inject(function(_$controller_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
}));
describe('$scope.grade', function() {
it('sets the strength to "strong" if the password length is >8 chars', function() {
var $scope = {};
var controller = $controller('PasswordController', { $scope: $scope });
$scope.password = 'longerthaneightchars';
$scope.grade();
expect($scope.strength).toEqual('strong');
});
});
});
now i am making service and factory, is there any equivalent to ____$controller____ for service and factory? so i can inject it with something else like:
var controller = $controller('PasswordController', { $scope: $scope });
and change the inner functions of the dependency so i can test it, or is there any better approach?
Edit: to make question more clear
here is the example of the question:
i have this:
var app = angular.module("app").service("MyService",["$scope","$http",function($scope,$http){
this.myFunction = function(){
$http.get("/myApi/1");
}
}]);
how do i use the equivalent of
var controller = $controller('PasswordController', { $scope: $scope });
so i can inject $scope and $http with something else to myService?
You can't inject dependencies to factories or services on the go, but you can mock the dependencies with your custom objects and have angular substitute them automatically. You can use $provide for that. Here is an example:
angular.module('app').service('some', function(dependencyService) {
});
When testing:
beforeEach(module(function($provide) {
$provide.value('dependencyService', {
});
}));
After doing a workarround, i found out from https://www.sitepoint.com/unit-testing-angularjs-services-controllers-providers/ about the service. i tested out the tutorial here and here is the test script:
(function () {
angular.module('services', [])
.service('sampleSvc', ['$window', 'modalSvc', function ($window, modalSvc) {
this.showDialog = function (message, title) {
if (title) {
modalSvc.showModalDialog({
title: title,
message: message
});
} else {
$window.alert(message);
}
};
}]);
describe("Testing service", function () {
var mockWindow, mockModalSvc, sampleSvcObj;
beforeEach(module(function ($provide) {
$provide.service('$window', function () {
this.alert = jasmine.createSpy('alert');
});
$provide.service('modalSvc', function () {
this.showModalDialog = jasmine.createSpy('showModalDialog');
});
}, 'services'));
beforeEach(inject(function ($window, modalSvc, sampleSvc) {
mockWindow = $window;
mockModalSvc = modalSvc;
sampleSvcObj = sampleSvc;
}));
it('should show alert when title is not passed into showDialog', function () {
var message = "Some message";
sampleSvcObj.showDialog(message);
expect(mockWindow.alert).toHaveBeenCalledWith(message);
expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled();
});
it('should show modal when title is passed into showDialog', function () {
var message = "Some message";
var title = "Some title";
sampleSvcObj.showDialog(message, title);
expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({
message: message,
title: title
});
expect(mockWindow.alert).not.toHaveBeenCalled();
});
});
})();
and i try my own test script:
(function () {
describe("Testing service", function () {
var mockHttp, mockCookies, mockApi;
beforeEach(function () {
module(function ($provide) {
$provide.service('$http', function () {
this.defaults = {
headers: {
common: {
}
}
};
});
$provide.service('$cookies', function () {
});
});
module('timesheet');
});
beforeEach(inject(function ($http, $cookies, APIService) {
mockHttp = $http;
mockCookies = $cookies;
mockApi = APIService;
}));
it('Test Service', function () {
});
});
})();
apparently in somewhere in my code, there is an app.run which inside do the
$http.defaults.headers.common.Authorization = 'Bearer ' + $cookies.get('sessionToken');
and causes the error the moment i inject the $http with something else because headers not defined, i thought it was from my own test script because they are using same name, but apparently this is the one causing problem.
So, actually the moment we load in testing mode, the angularjs still do the whole running of application, in which i forgot about this one.

How to test that my service returns data with jasmine and angularjs

I've begun using jasmine to test my controllers in angularjs however after reading some tutorials I am a bit stuck.
I have this simple angularjs controller called jasmineController
(function () {
"use strict";
var myAppModule = angular.module('myApp');
myAppModule.controller('jasmineController', ['$scope', 'genericService',
function ($scope, genericService) {
$scope.name = 'Superhero';
$scope.counter = 0;
$scope.$watch('name', function (newValue, oldValue) {
$scope.counter = $scope.counter + 1;
});
$scope.testPromise = function() {
return genericService.getAll("dashboard", "currentnews", null, null, null);
}
$scope.getNewsItems = function () {
genericService.getAll("dashboard", "currentnews", null, null, null).then(function (data) {
$scope.name = 'Superhero';
$scope.newsItems = data;
});
}
}
]);
})();
In my jasmine test I wanted to call getNewsItems and check that it can call genericService.getAll and that $scope.newsItems is assigned some data. I understand that I would be mocking out the service and I won't actually call it.
Here is my spec
describe("test", function () {
// Declare some variables required for my test
var controller, scope, genericService;
// load in module
beforeEach(module("myApp"));
beforeEach(inject(function ($rootScope, $controller, _genericService_) {
genericService = _genericService_;
// assign new scope to variable
scope = $rootScope.$new();
controller = $controller('jasmineController', {
'$scope': scope
});
}));
it('sets the name', function () {
expect(scope.name).toBe('Superhero');
});
it('should assign data to scope', function() {
//var fakeHttpPromise = {success: function () { }};
scope.getNewsItems();
spyOn(genericService, 'getAll');
expect(genericService.getAll).toHaveBeenCalledWith('dashboard', 'currentnews');
});
});
I've got a spyon for genericService.getall() but apart from that I am a bit stuck with checking that my scope variable is assigned a value.
I also get this stack trace:
Error: Expected spy getAll to have been called with [ 'dashboard', 'currentnews' ] but it was never called.
at stack (file:///C:/Users/nickgowdy/Local%20Settings/Application%20Data/Microsoft/VisualStudio/12.0/Extensions/4sg2jkkc.gb4/TestFiles/jasmine/v2/jasmine.js:1441:11)
at buildExpectationResult (file:///C:/Users/nickgowdy/Local%20Settings/Application%20Data/Microsoft/VisualStudio/12.0/Extensions/4sg2jkkc.gb4/TestFiles/jasmine/v2/jasmine.js:1408:5)
at expectationResultFactory (file:///C:/Users/nickgowdy/Local%20Settings/Application%20Data/Microsoft/VisualStudio/12.0/Extensions/4sg2jkkc.gb4/TestFiles/jasmine/v2/jasmine.js:533:11)
at Spec.prototype.addExpectationResult (file:///C:/Users/nickgowdy/Local%20Settings/Application%20Data/Microsoft/VisualStudio/12.0/Extensions/4sg2jkkc.gb4/TestFiles/jasmine/v2/jasmine.js:293:5)
at addExpectationResult (file:///C:/Users/nickgowdy/Local%20Settings/Application%20Data/Microsoft/VisualStudio/12.0/Extensions/4sg2jkkc.gb4/TestFiles/jasmine/v2/jasmine.js:477:9)
at Anonymous function (file:///C:/Users/nickgowdy/Local%20Settings/Application%20Data/Microsoft/VisualStudio/12.0/Extensions/4sg2jkkc.gb4/TestFiles/jasmine/v2/jasmine.js:1365:7)
at Anonymous function (file:///C:/Projects/2013/AMT2015/AMT2015.WebAPP/Scripts/tests/controllers/dashboardControllerSpec.js:49:9)
at attemptSync (file:///C:/Users/nickgowdy/Local%20Settings/Application%20Data/Microsoft/VisualStudio/12.0/Extensions/4sg2jkkc.gb4/TestFiles/jasmine/v2/jasmine.js:1759:9)
at QueueRunner.prototype.run (file:///C:/Users/nickgowdy/Local%20Settings/Application%20Data/Microsoft/VisualStudio/12.0/Extensions/4sg2jkkc.gb4/TestFiles/jasmine/v2/jasmine.js:1747:9)
at QueueRunner.prototype.execute (file:///C:/Users/nickgowdy/Local%20Settings/Application%20Data/Microsoft/VisualStudio/12.0/Extensions/4sg2jkkc.gb4/TestFiles/jasmine/v2/jasmine.js:1733:5)
You need to put the spy first before calling the test function. And you are you actually passing more parameters to the service function. So you need to test with the exact parameter list.
it('should assign data to scope', function() {
//var fakeHttpPromise = {success: function () { }};
spyOn(genericService, 'getAll');
scope.getNewsItems();
expect(genericService.getAll).toHaveBeenCalledWith('dashboard', 'currentnews',null,null,null);
});
I ended up doing this:
describe("test", function () {
// Declare some variables required for my test
var controller, scope, genericService;
// load in module
beforeEach(module("myApp"));
beforeEach(inject(function ($rootScope, $controller, _$q_, _genericService_) {
genericService = _genericService_;
var deferred = _$q_.defer();
deferred.resolve('resolveData');
spyOn(genericService, 'getAll').and.returnValue(deferred.promise);
scope = $rootScope.$new();
controller = $controller('jasmineController', {
'$scope': scope
});
}));
it('sets the name', function () {
expect(scope.name).toBe('Superhero');
});
it('should assign data to scope', function() {
//spyOn(genericService, 'getAll').and.callFake(function() {
//});
scope.getNewsItems();
scope.$apply();
expect(scope.newsItems).toBe('resolveData');
//expect(genericService.getAll).toHaveBeenCalledWith('dashboard', 'currentnews', null, null, null);
});
});
Because my test is more than just calling a service but handling a promise as well I had to inject $q. Then with spy on I say to call service and method and the return value is the deferred promise.
Finally I can look at the scope variable to see if anything is assigned with this line:
expect(scope.newsItems).toBe('resolveData');
Thanks to everyone that helped.

How to spy on anonymous function using Jasmine

I'm using Jasmine to test my angular application and want to spy on an anonymous function.
Using angular-notify service https://github.com/cgross/angular-notify, I want to know whether notify function have been called or not.
Here is my controller:
angular.module('module').controller('MyCtrl', function($scope, MyService, notify) {
$scope.isValid = function(obj) {
if (!MyService.isNameValid(obj.name)) {
notify({ message:'Name not valid', classes: ['alert'] });
return false;
}
}
});
And here is my test:
'use strict';
describe('Test MyCtrl', function () {
var scope, $location, createController, controller, notify;
beforeEach(module('module'));
beforeEach(inject(function ($rootScope, $controller, _$location_, _notify_) {
$location = _$location_;
scope = $rootScope.$new();
notify = _notify_;
notify = jasmine.createSpy('spy').andReturn('test');
createController = function() {
return $controller('MyCtrl', {
'$scope': scope
});
};
}));
it('should call notify', function() {
spyOn(notify);
controller = createController();
scope.isValid('name');
expect(notify).toHaveBeenCalled();
});
});
An obviously return :
Error: No method name supplied on 'spyOn(notify)'
Because it should be something like spyOn(notify, 'method'), but as it's an anonymous function, it doesn't have any method.
Thanks for your help.
Daniel Smink's answer is correct, but note that the syntax has changed for Jasmine 2.0.
notify = jasmine.createSpy().and.callFake(function() {
return false;
});
I also found it useful to just directly return a response if you only need a simple implementation
notify = jasmine.createSpy().and.returnValue(false);
You could chain your spy with andCallFake see:
http://jasmine.github.io/1.3/introduction.html#section-Spies:_andCallFake
//create a spy and define it to change notify
notify = jasmine.createSpy().andCallFake(function() {
return false;
});
it('should be a function', function() {
expect(typeof notify).toBe('function');
});
controller = createController();
scope.isValid('name');
expect(notify).toHaveBeenCalled();

$scopeProvider <- $scope/ Unknown provider

I testing my angular-application with jasmine(http://jasmine.github.io/2.0/) and getting next error:
Unknown provider: $scopeProvider <- $scope
I know, that it's incorrect to build dependency with scope in filters, services, factories, etc., but I use $scope in controller!
Why am i getting this error? controller looks like
testModule.controller('TestCont', ['$filter', '$scope', function($filter, $scope){
var doPrivateShit = function(){
console.log(10);
};
this.lol = function(){
doPrivateShit();
};
this.add = function(a, b){
return a+b;
};
this.upper = function(a){
return $filter('uppercase')(a);
}
$scope.a = this.add(1,2);
$scope.test = 10;
$scope.search = {
};
}]);
and my test's code:
'use strict';
describe('testModule module', function(){
beforeEach(function(){
module('testModule');
});
it('should uppercase correctly', inject(function($controller){
var testCont = $controller('TestCont');
expect(testCont.upper('lol')).toEqual('LOL');
expect(testCont.upper('jumpEr')).toEqual('JUMPER');
expect(testCont.upper('123azaza')).toEqual('123AZAZA');
expect(testCont.upper('111')).toEqual('111');
}));
});
You need to manually pass in a $scope to your controller:
describe('testModule module', function() {
beforeEach(module('testModule'));
describe('test controller', function() {
var scope, testCont;
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
testCont = $controller('TestCont', {$scope: scope});
}));
it('should uppercase correctly', function() {
expect(testCont.upper('lol')).toEqual('LOL');
expect(testCont.upper('jumpEr')).toEqual('JUMPER');
...
});
});
});
Normally, a $scope will be available as an injectable param only when the controller is attached to the DOM.
You need to associate somehow the controller to the DOM (I'm mot familiar with jasmine at all).
I am following a video tutorial from egghead (link bellow) which suggest this approach:
describe("hello world", function () {
var appCtrl;
beforeEach(module("app"))
beforeEach(inject(function ($controller) {
appCtrl = $controller("AppCtrl");
}))
describe("AppCtrl", function () {
it("should have a message of hello", function () {
expect(appCtrl.message).toBe("Hello")
})
})
})
Controller:
var app = angular.module("app", []);
app.controller("AppCtrl", function () {
this.message = "Hello";
});
I am posting it because in the answer selected we are creating a new scope. This means we cannot test the controller's scope vars, no?
link to video tutorial (1min) :
https://egghead.io/lessons/angularjs-testing-a-controller

Angularjs Unit Test for Service

I am consuming a service for which i am writing unit test case. When i inject the service & call the function from controller, i do not get the data. I am beginner to write cases.
Here is my code.
StatesList Service
angular.module('myApp').factory('StatesList', ['$resource', function($resource) {
return $resource('/api/states');
}]);
Controller
$scope.statesList = function () {
StatesList.query(function (states) {
// Brings all states
$scope.states = states;
});
};
Test
describe('States List', function () {
var ctrl, scope, statesService;
beforeEach(function () {
module('myApp');
inject(function ($rootScope, $controller, StatesList) {
scope = $rootScope.$new();
statesService = StatesList;
ctrl = $controller('StatesCtrl', { $scope: scope, StatesList: statesService });
});
});
it('should have practice list to be null', function () {
console.log('List of States');
scope.statesList();
console.log(scope.states); // I don't see any data here
expect(scope.states).not.toBeNull();
});
Output in WebStorm
'List of States'
undefined
Why the states don't get displayed. By using POSTMAN data can be seen.
StatesList.query() is an asynchronous http call, so you need to use mock $httpBackend service from ngMock module in your test. Add angular-mock.js to your test config, then try this:
describe('States List', function () {
var ctrl, scope, statesService, $httpBackend;
beforeEach(function () {
module('myApp');
inject(function ($rootScope, $controller, StatesList, _$httpBackend_) {
scope = $rootScope.$new();
statesService = StatesList;
ctrl = $controller('StatesCtrl', { $scope: scope, StatesList: statesService});
$httpBackend = _$httpBackend_;
});
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should have practice list to be null', function () {
$httpBackend.expectGET('/api/states').respond([{ // ask mock $httpBackend to respond with fake data
name: 'State 1'
}, {
name: 'State 2'
}]);
console.log('List of States');
scope.statesList();
$httpBackend.flush(); // flush the http request to send fake data back to StatesList.query()
console.log(scope.states); // I don't see any data here
expect(scope.states).not.toBeNull();
});
});

Categories