Jasmine and angularjs, using $cookies in jasmine - cookies

I am fairly new to jasmine and wanted to create a test for the following below, I created the code in the test section but I get "TypeError: Cannot set property 'username' of undefined"..
I created a global namespace 'cp' in apps.js and used that in the service and controller.
//controller
cp.controller = {};
cp.controller.LoginController = function($scope, $location, $cookies){
$scope.signIn = function(){
$cookies.user = $scope.form.username;
user.set($scope.form.username);
$location.hash( "home" );
}
};
//service
cp.serviceFactory = {};
cp.serviceFactory.user = function user( $cookies){
var userName = $cookies.user;
return{
set: function(name){
userName = name;
},
get: function(){
return userName;
}
}
};
//test script
describe('Cameo Controllers', function() {
describe('LoginController', function(){
var scope, cookies, ctrl, $httpBackend;
beforeEach(module('CameoPaaS'));
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller, $cookies) {
$httpBackend = _$httpBackend_;
// cookies = $cookies.username;
scope = $rootScope.$new();
cookies = scope.$cookies;
ctrl = $controller(cp.controller.LoginController, {$scope: scope, $cookies: cookies});
}));
it('should log the user into the system', function() {
expect(scope.username).toBeUndefined();
scope.form.username = 'me';
scope.signIn();
//expect(user).toBe(undefined);
});
});
});
Question: how do I define and set the value for the $cookies.username in the test script to get around the error.

First off make sure you are including angular-cookies.js these were separated from main distro in 1.0.0rc3
If it were me, I would wrap the cookies handling into a service and then use jasmine to mock/spy on the your cookie-wrapper service implementation. You might find this post helpful. Also, I found this testing cookies in unit and e2e. IMHO the problem with this is that it is too close to the metal, having to work with the browser cookies directly.

I also run through the same problem, here is the workaround -
beforeEach(inject(function($cookies){
$cookies.username = 'AngularJs';
}));
Please suggest if there is any better way.

Related

Angular Unit Testing a resource request

I am attempting to perform a unit test on this controller.
angular.module('app.dashboard', [])
.controller('DashboardController', ['$scope', 'myAppService'], function($scope, myAppService) {
var _data = myAppService.requests.get(function() {
$scope.requests = _data.requests;
});
});
myAppService is a service based on ngResource.
I want to test for the number of requests. I spent all day figuring out how to get $httpBackend injected, now I'm hung up on properly measuring the data.
beforeEach(inject(function ($controller, $rootScope, $injector) {
$httpBackend = $injector.get('$httpBackend');
$httpBackend.when('GET', '/api/requests').respond(
{requests: [{sender: 'joe', message: 'help'}, {sender: 'larry', message: 'SOS'}]}
);
});
it('should have a properly working Dashboard controller', inject(function($rootScope, $controller, $httpBackend) {
var $scope = $rootScope.$new();
var ctrl = $controller('DashboardController', {
$scope : $scope
});
expect($scope.requests.length).toBe(2);
}));
Any assistance would be greatly, massively appreciated.
For the most part, everything looks ok. There are two things that I think might be missing from your code though.
First, this method doesn't look quite right:
var _data = myAppService.requests.get(function() {
$scope.requests = _data.requests;
});
If you are providing a callback function, shouldn't that be passing in the data:
myAppService.requests.get(function(_data) {
$scope.requests = _data.requests;
});
It is hard to tell without seeing your myAppService code (that is using $resource).
Second, in order to flush through the ajax response you need to call $httpBackend.flush(); before you make your assertion with expect:
var $scope = $rootScope.$new();
var ctrl = $controller('DashboardController', {
$scope : $scope
});
httpBackend.flush();
expect($scope.requests.length).toBe(2);
From the $httpBackend docs:
flush(count):
Flushes all pending requests using the trained responses.
Hope this helps.

Angularjs Unit Testing: Am I doing it right?

I started to write unit tests for my angular app.
However it seems to me that I use a lot of boilerplate code to init and test the controller.
In this Unit Test I want to test if a model from the scope is sent to the Api when I execute a function.
I needed 20 lines of code for this. This makes it inconvenient to write unit tests that do only one thing.
Do you have any tips on getting the code size to a smaller chunk?
This is my current unit test:
'use strict';
describe('controllers', function(){
beforeEach(module('kronos'));
describe('CustomerSignupCtrl', function() {
it('should send customer to Api on submit', inject(function($controller) {
var scope = {};
var $location = {};
var Api = {
signupCustomer: function(customer) {
expect(customer).toEqual({attrs: "customerdata"});
return {
success: function() { return this; },
error: function() { return this; }
};
}
};
var ctrl = $controller('CustomerSignupCtrl', {
$scope: scope,
$location: location,
Api: Api});
scope.customer = {attrs: "customerdata"};
scope.signup();
}));
});
});
What I don't like in particular are the following points
I need to init the all dependencies and it doesn't matter if I use them or not
The Api returns a promise that I only need because the controller is expecting the promise
I need to init the controller.
How can I make this code shorter and more explicit?
Edit: I just noticed I can ignore the $location Service for this unit test. Great
Edit2: I found out about angular-app, which serves as a good practice example app. There you can find specs with jasmine, which are really nice written.
Use another beforeEach method in your describe scope to set up scope, $location, controller etc, then just change them in your test as you need to. Js is dynamic so all should be fine.
You can also extract each object that you set up into a function so that you can reinitialise them in a test if you need to.
describe('controllers', function(){
beforeEach(module('kronos'));
describe('CustomerSignupCtrl', function() {
var controller, scope, $location, Api;
beforeEach(function(){
scope = {};
$location = {};
Api = {
signupCustomer: function(customer) {
expect(customer).toEqual({attrs: "customerdata"});
return {
success: function() { return this; },
error: function() { return this; }
};
}
};
controller = makeController();
})
function makeController(){
inject(function($controller){
controller = $controller('CustomerSignupCtrl', {
$scope: scope,
$location: location,
Api: Api});
});
}
it('should send customer to Api on submit', function() {
scope.customer = {attrs: "customerdata"};
scope.signup();
});
});
});
You can not shorten your code much. Things like initialization, mocking and assertion have to be done at some place. But you can improve the readability of your code by decoupling initialization and test code. Something like this:
describe('CustomerSignupCtrl', function(){
var controller, scope, location, api;
beforeEach(module('kronos'));
// initialization
beforeEach(inject(function($controller, $rootScope, $location, Api){
scope = $rootScope.$new();
location = $location;
api = Api;
controller = $controller('CustomerSignupCtrl', {
$scope: scope, $location: location, Api: api});
}));
// test
it('should send customer to Api on submit', function() {
scope.customer = {attrs: "customerdata"};
spyOn(api,'signupCustomer').andCallFake(function(customer) {
return {
success: function() { return this; },
error: function() { return this; }
};
});
scope.signup();
expect(api.signupCustomer).toHaveBeenCalledWith(scope.customer);
});
});

AngularJS inject service mock inside service tests

I have been trying to test a service to no avail for some time now and was hoping for some help. Here is my situation:
I have a service looking a little like this
myModule.factory('myService', ['$rootScope', '$routeParams', '$location', function($rootScope, $routeParams, $location) {
var mySvc = {
params: {}
}
// Listen to route changes.
$rootScope.$on('$routeUpdate', mySvc.updateHandler);
// Update #params when route changes
mySvc.updateHandler = function(){ ... };
...
...
return mySvc;
}]);
And I want to mock the services injected into 'myService' before the service gets injected into my tests so I can test the initialization code below
var mySvc = {
params: {}
}
// Listen to route changes.
$rootScope.$on('$routeUpdate', mySvc.updateHandler);
I am using Jasmine for tests and mocks. This is what I came up with for now
describe('myService', function(){
var rootScope, target;
beforeEach(function(){
rootScope = jasmine.createSpyObj('rootScope', ['$on']);
module('myModule');
angular.module('Mocks', []).service('$rootScope', rootScope );
inject(function(myService){
target = myService;
});
});
it('should be defined', function(){
expect(target).toBeDefined();
});
it('should have an empty list of params', function(){
expect(target.params).toEqual({});
});
it('should have called rootScope.$on', function(){
expect(rootScope.$on).toHaveBeenCalled();
});
});
This doesn't work though. My rootscope mock is not replacing the original and the Dependency Injection doc is confusing me more than anything.
Please help
I would spy on the actual $rootScope instead of trying to inject your own custom object.
var target, rootScope;
beforeEach(inject(function($rootScope) {
rootScope = $rootScope;
// Mock everything here
spyOn(rootScope, "$on")
}));
beforeEach(inject(function(myService) {
target = myService;
}));
it('should have called rootScope.$on', function(){
expect(rootScope.$on).toHaveBeenCalled();
});
I've tested this in CoffeScript, but the code above should still work.
You could create a RootController and then inject it:
inject(function(myService, $controller){
target = myService;
$controller('rootController', {
$scope : $rootScope.$new(),
$rootScope : myService
});
});
With this approach you can access $rootScope functions from your 'myService';
Such 'myService.$on()'
I just made it, let me know if help is needed.

How to mock angular $resource in jasmine tests

I am trying to test a controller which uses angular's $resource.
function PermissionsCtrl($scope, $resource, $cookies) {
var Object = $resource('/v1/objects/:id');
loadObjects();
function loadObjects() {
$scope.myAppObjects = new Array();
var data = AppObject.get({find: '{"app_id": '+wtm.app_id+'}'},
function(){
if (data.results) {
for(var i = 0; i< data.results.length; i++) {
if(!data.results[i].is_deleted) {
(function(i){
$scope.objects(data.results[i]);
}(i));
}
}
}
},
function(error){console.log(error);});
}
And here is the test code.
var apiServer = "...";
var app_id = 999
var mock_object_data = {...};
describe('apps permissionsCtrl', function(){
var scope, ctrl, $httpBackend;
// Create a matcher for comparing data
beforeEach( function() {
this.addMatchers({
toEqualData: function(expected) {
return angular.equals(this.actual, expected);
}
});
});
// Create the controller with injected data/services
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller, $resource) {
$httpBackend = _$httpBackend_;
// cookie data to inject
var cookies = new Array();
cookies['id'] = '...'; // just needs to be declared
$httpBackend.expectGET(apiServer+'/v1/app_objects? find=%7B%22app_id%22:'+app_id+'+%7D&access_token=' + cookies['id'])
.respond( mock_object_data );
var $injector = angular.injector(['ng', 'ngResource']);
var $resource = $injector.get('$resource');
scope = $rootScope.$new();
ctrl = $controller(PermissionsCtrl, {$scope: scope, $cookies: cookies, $resource: $resource});
}));
it('should put object data into $scope', function() {
$httpBackend.flush();
expect(scope.objects).toEqualData( mock_object_data );
});
});
When I run this I get
Error: Unknown provider: $resourceProvider <- $resource
at the line where I try to create my controller. I don't understand how to inject this into my controller and no matter what I try I get the same error. A couple things I've tried are
Declaring a an empty mock object and passing it through similar to my cookies variable. I figure this is probably a bad solution anyway since I actually want to use the service.
Mimicking the scope mock and passing it into my inject function and passing $resource.$new() to my controller.
Doing nothing and hoping that httpBackend would cover it since that's what ultimately gets called anyway. Vojta Jína made it sound like that would work but no dice.
Mild epithets. Satisfying but not very effective.
After more reading and more experimenting it seems the right way to do this is to abstract the use of $resource out of the controller. In my case I wrote a service that relies on $resource and then inject that service into my controller. Meanwhile I test that service separately from my controller. Better practice all around.
My service declaration:
angular.module('apiModule', ['localResource', 'ngCookies'])
.factory('apiService', function($resource, $cookies) {
and in my unit tests I pass it through in a beforeEach setup function
beforeEach(module('apiModule'));
Try to instantiate $resource with the following code:
var $injector = angular.injector(['ng', 'ngResource']);
var $resource = $injector.get('$resource');
Similar for other services, except when they are in other modules still. Then add that module to the array.
More info

How to mock an function of an Angular service in Jasmine

I have the following angular controller
function IndexCtrl($scope, $http, $cookies) {
//get list of resources
$http.get(wtm.apiServer + '/v1/developers/me?access_token=' + $cookies['wtmdevsid']).
success(function(data, status, headers, config) {
// snip
}).
error(function(data, status, headers, config) {
// snip
});
$scope.modal = function() {
// snip
}
return;
}
What I am trying to do is mock the get method on the $http service. Here's my unit test code:
describe('A first test suite', function(){
it("A trivial test", function() {
expect(true).toBe(true);
});
});
describe('Apps', function(){
describe('IndexCtrl', function(){
var scope, ctrl, $httpBackend;
var scope, http, cookies = {wtmdevsid:0};
beforeEach(inject(function($injector, $rootScope, $controller, $http) {
scope = $rootScope.$new();
ctrl = new $controller('IndexCtrl', {$scope: scope, $http: $http, $cookies: cookies});
spyOn($http, 'get');
spyOn(scope, 'modal');
}));
it('should create IndexCtrl', function() {
var quux = scope.modal();
expect(scope.modal).toHaveBeenCalled();
expect($http.get).toHaveBeenCalled();
});
});
});
When I run this I get
ReferenceError: wtm is not defined.
wtm is a global object and of course it wouldn't be defined when I run my test because the code that it is declared in is not run when I run my test. What I want to know is why the real $http.get function is being called and how do I set up a spy or a stub so that I don't actually call the real function?
(inb4 hating on globals: one of my coworkers has been tasked with factoring those out of our code :) )
You need to wire up the whenGET method of your $httpBackend in advance of your test. Try setting it up in the beforeEach() function of your test... There is a good example here under "Unit Testing with Mock Backend".
I suggest all globals used the way you described here should be used through the $window service.
All global variables that are available, such as as window.wtm, will also be available on $window.atm.
Then you can stub out your wtm reference completely and spy on it the same way you already described:
var element, $window, $rootScope, $compile;
beforeEach(function() {
module('fooApp', function($provide) {
$provide.decorator('$window', function($delegate) {
$delegate.wtm = jasmine.createSpy();
return $delegate;
});
});
inject(function(_$rootScope_, _$compile_, _$window_) {
$window = _$window_;
$rootScope = _$rootScope_;
$compile = _$compile_;
});
});
Maybe you could create a custom wrapper mock around $httpBackend that handles your special needs.
In detail, Angular overwrites components of the same name with a last-come first-served strategy, this means that the order you load your modules is important in your tests.
When you define another service with the same name and load it after the first one, the last one will be injected instead of the first one. E.g.:
apptasticMock.service("socket", function($rootScope){
this.events = {};
// Receive Events
this.on = function(eventName, callback){
if(!this.events[eventName]) this.events[eventName] = [];
this.events[eventName].push(callback);
}
// Send Events
this.emit = function(eventName, data, emitCallback){
if(this.events[eventName]){
angular.forEach(this.events[eventName], function(callback){
$rootScope.$apply(function() {
callback(data);
});
});
};
if(emitCallback) emitCallback();
}
});
This service offers the exact same interface and behaves exactly like the original one except it never communicates via any socket. This is the service we want to use for testing.
With the load sequence of angular in mind, the tests then look like this:
describe("Socket Service", function(){
var socket;
beforeEach(function(){
module('apptastic');
module('apptasticMock');
inject(function($injector) {
socket = $injector.get('socket');
});
});
it("emits and receives messages", function(){
var testReceived = false;
socket.on("test", function(data){
testReceived = true;
});
socket.emit("test", { info: "test" });
expect(testReceived).toBe(true);
});
});
The important thing here is that module('apptasticMock') gets executed after module('apptastic'). This overwrites the original socket implementation with the mocked one. The rest is just the normal dependency injection procedure.
This article I wrote could be interesting for you, as it goes into further details.