Angularjs injecting $resource into factory for Testing with jasmine - unit-testing

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.

Related

How do I unit test a helper that uses a service?

I'm trying to unit test a helper that uses a service.
This is how I inject the service:
export function initialize(container, application) {
application.inject('view', 'foobarService', 'service:foobar');
}
The helper:
export function someHelper(input) {
return this.foobarService.doSomeProcessing(input);
}
export default Ember.Handlebars.makeBoundHelper(someHelper);
Everything works until here.
The unit test doesn't know about the service and fails. I tried to:
test('it works', function(assert) {
var mockView = {
foobarService: {
doSomeProcessing: function(data) {
return "mock result";
}
}
};
// didn't work
var result = someHelper.call(mockView, 42);
assert.ok(result);
});
The error:
Died on test #1 at http://localhost:4200/assets/dummy.js:498:9
at requireModule (http://localhost:4200/assets/vendor.js:79:29)
TypeError: undefined is not a function
Everything is correct, the only change needed was:
var result = someHelper.call(mockView, "42");

Multiple Manual Mocks of CommonJS Modules with Jest

I saw the documentation for Jest's mocks using the mocks folder, but I want to be able to mock a module with one mock in one test and mock that same module with another mock in another test.
For example, with rewire and jasmine, you could do something like this:
//module2.js
module.exports = {
callFoo: function () {
require('moduleToMock').foo();
}
};
//module2Test.js
describe("test1", function () {
var mock;
beforeEach(function () {
var rewire = require('rewire');
mock = jasmine.createSpyObj('mock', ['foo']);
});
it("should be mocked with type1", function () {
mock.foo.and.returnValue("type1");
rewire('moduleToMock', mock);
var moduleUsingMockModule = require('module2');
expect(moduleUsingMockModule.callFoo()).toEqual("type1");
});
});
describe("test2", function () {
it("should be mocked with type2", function () {
mock.foo.and.returnValue("type2");
rewire('moduleToMock', mock);
var moduleUsingMockModule = require('module2');
expect(moduleUsingMockModule.callFoo()).toEqual("type2");
});
});
Is this possible to do with Jest? The difference is I define the mock within the test, not in some external folder that is used for all tests.
Yes, your mock will look like this:
module.exports = {
foo: jest.genMockFunction();
}
Then you will be able to configure a custom behaviour in your test cases:
var moduleToMock = require('moduleToMock');
describe('...', function() {
it('... 1', function() {
moduleToMock.foo.mockReturnValue('type1')
expect(moduleToMock.foo).toBeCalled();
expect(moduleUsingMockModule.callFoo()).toEqual("type1");
});
it('... 2', function() {
moduleToMock.foo.mockReturnValue('type2')
expect(moduleToMock.foo).toBeCalled();
expect(moduleUsingMockModule.callFoo()).toEqual("type2");
});
});

AngularJS and Virtual Paths

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

Testing angular factories with jasmine

I have a problem with jasmine and angular. I have to test my factories.
I have made simplidfied project on plunker.
Here is demo app:
http://plnkr.co/edit/Agq4xz9NmYeEDWoJguxt
Here is demo test:
http://plnkr.co/edit/ALVKdXO00IEDaKIjMY6u
The first problem is how to make spec runner working without any specification to test.
When you run demo test plunker you will see error:
TypeError: myDataRaw is undefined in http: //run.plnkr.co/Kqz4tGMsdrxoFNKO/app.js (line 39)
Somebody can help?
Thanks
The problem is
In your production code
var dataLoadedPromise = $http.get('myData.xml', {transformResponse: transformResponse});
you expect the return from the server is XML data not json, so when you mock the HTTP, you should use XML instead of Json.
Here is the working code:
describe("controller: MyController", function () {
beforeEach(function () {
module("myApp");
});
var dataFactory;
beforeEach(inject(function ($injector, $controller, $rootScope, $location, $httpBackend) {
this.$location = $location;
this.$httpBackend = $httpBackend;
this.scope = $rootScope.$new();
dataFactory = $injector.get('dataFactory');
this.$httpBackend.expectGET('myData.xml').respond(
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?><myData><persons><person><firstName>Pera</firstName><lastName>Peric</lastName><email>perica#gmail.com</email><score>22</score></person></persons></myData>");
$controller('MyController', {
$scope: this.scope,
$location: $location,
myData: dataFactory
});
}));
afterEach(function () {
this.$httpBackend.verifyNoOutstandingRequest();
this.$httpBackend.verifyNoOutstandingExpectation();
});
describe("successfully parsed persons", function () {
it("should have 2 persons", function () {
dataFactory.getData();
this.$httpBackend.flush();
});
});
});
Updated Demo

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.