AngularJS and Virtual Paths - unit-testing

How do you deal with Virtual Paths in AngularJS?
For example in my test environment, my base url is http://localhost/AppName/ and in production the root becomes http://www.mysite.com/.
So in dev, a $http.get goes to say http://localhost/AppName/Controller/Action but in prod that path does not exist, it needs to be http://www.mysite.com/Controller/Action.
How do you deal with $http calls and also, unit tests when mocking the backend with $httpBackend?
I have tried this in the _Layout.cshtml (using ASP.NET MVC) but that does not work in the unit tests:
<script>
(function () {
'use strict';
var app = angular.module('cs').value('urlHelper', {
getUrl: function (url) {
var basePath = '#VirtualPathUtility.ToAbsolute("~/")';
return basePath + url;
}
});
}());
</script>
Also have tried:
(function () {
'use strict';
var app = angular.module('cs', []);
app.run(['$rootScope', function ($rootScope) {
$rootScope.basePath = '/';
}]);
}());
Any ideas to not have to worry about switching the root path somewhere before deploying to prod?

basePath can be a value on its own and urlHelper a service that depends on basePath.
in the server side generated page, your server will inject whatever value VirtualPathUtility finds and in your tests you will manually inject whatever works for you.
razor:
<script>
angular.module('cs').value('basePath', '#VirtualPathUtility.ToAbsolute("~/")');
</script>
other js file:
angular.module('cs').factory('urlHelper', function (basePath) {
return {
getUrl: function (url) {
return basePath + url;
}
};
});
test:
var urlHelper;
beforeEach(function() {
module(function($provide) {
$provide.value('basePath', '/');
});
inject(function($injector) {
urlHelper = $injector.get('urlHelper');
});
});

Related

Mocking $window in angularjs with qunit

I'm pretty new to angular and been wanting to test drive and I've hit a snag mocking out $window. The item in which I'm attempting to test is very simple but important -- I need to know if localStorage is there or not and need to be able to fake out $window to do so.
The code is very basic so far and what I have is this for the service ...
'use strict';
mainApp.factory('somedataStorage',function($window) {
var local = $window.localStorage;
return {
hasLocal: function() {
return local != undefined;
},
};
});
How I'm testing it is this ...
(function () {
var fakeWin = {
localStorage: null
};
var $injector = angular.injector(['ngMock', 'ng', 'mainApp']);
//var $window = $injector.get('$window');
var init = {
setup: function () {
//this.$window = fakeWin;
},
}
module('LocalStorageTests', init);
test("if localstorage isn't there, say so", function () {
var $service = $injector.get('somedataStorage' /*, {$window: fakeWin} */);
ok(!$service.hasLocal, "no local storage");
});
})();
So what am I missing?

Angularjs injecting $resource into factory for Testing with jasmine

I am writing my unit tests to test factory developed as below
angular.module("MyApp.Factory",[]).factory("factory",function($resource, baseUrl) {
return {
someService: function () {
return $resource(baseUrl + "/someUri");
}
};
});
jasmine spec for the same
describe("My App Factory Tests", function () {
var $factory;
beforeEach(function () {
module("MyApp.Factory");
inject(function (factory) {
$factory = factory;
});
});
it("Factory must be defined", function () {
expect($factory).toBeDefined();
});
});
but i end up with the below error from the jasmine html test page
Error: Unknown provider: $resourceProvider <- $resource <- eligibilityService
Please suggest the right way to test the factory
ngResource is defined in the separate module. Please check the documentation http://docs.angularjs.org/api/ngResource
In other words you have to include <script src="angular-resource.js"> and add ngResource dependency to MyApp.Factory module - angular.module("MyApp.Factory", ["ngResource"]).
ps. in the specs you could surround service name with underscores so that we can locally assign a local variable of the same name as the service. For example:
var factory;
beforeEach(function () {
module("MyApp.Factory");
inject(function (_factory_) {
factory = _factory_;
});
});
According to angularjs coding standards $ should not be used for naming custom directives.

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 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.

Jasmine and angularjs, using $cookies in jasmine

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.