AngularJS factory unit testing with dependencies - javascript

I am trying to test some AngularJS factories with Jasmine. It works fine for factories that don't have any dependencies. One of my factories uses Angular Material's $mdToast as dependency.
The factory:
(function() {
'use strict';
angular
.module('myModule')
.factory('ToastFactory', ToastFactory);
ToastFactory.$inject = ['$mdToast'];
function ToastFactory($mdToast) {
var service = {
showToast1: showToast1,
showToast2: showToast2
};
return service
function showToast1() {
return $mdToast.show($mdToast.build({
templateUrl: 'path'
}));
}
function showToast2() {
return $mdToast.show($mdToast.build({
templateUrl: 'path'
}));
}
}
})();
And here is one of the working tests for another factory without dependencies.
describe('myFactory', function() {
//Injector Service
var $injector;
//Set Module
beforeEach(function() {
angular.module('myModule');
});
//Inject injector service
beforeEach(inject(function() {
$injector = angular.injector(['myModule']);
}));
describe('SampleTest', function() {
it('should be true', function() {
//Arrange
var factory = $injector.get('myFactory');
//Act
var res = factory.testMethod();
//Assert
expect(res).toBe(true);
});
});
})
I know how to do it for controllers, but not for factories.

Related

Injecting dependencies into Angular services for testing

I'm trying to do some test driven development of an angular service I'm writing using Jasmine. However, I seem to have stumbled at the first hurdle, and I cannot get my $resource dependency to resolve.
The error I get is
Unknown provider: $resourceProvider <- $resource <- lookupService
My code is as follows
Module:
(function() {
'use strict';
angular
.module('common', ['ngSanitize', 'ngAnimate']);
})();
Service:
(function() {
'use strict';
angular
.module('common')
.service('lookupService', lookupService);
lookupService.$inject = ['$resource', 'api'];
function lookupService($resource, api) {
return {
getLookup: getLookup
};
function getLookup() {
return "something";
}
}
})();
Test:
describe('Service tests',
function () {
var lookupService, mockApi, $httpBackend;
//mocks
beforeEach(function () {
mockApi = { getUri: jasmine.createSpy() };
angular.mock.module('common',
function ($provide) {
$provide.value('api', mockApi);
});
});
//injects
beforeEach(function () {
inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
lookupService = $injector.get('lookupService');
});
});
//tests
it('should be able to return something',
inject(function () {
expect(lookupService.getLookup()).toEqual("something");
}));
});//Service tests
The angular-resource.js file is included in my runner file. I'm not sure where I'm going wrong, any help would be appreciated.
Thanks
ngResource must be a dependency:
(function() {
'use strict';
angular
.module('common', ['ngSanitize', 'ngAnimate', 'ngResource']);
})();

Angular - Testing http services with httpBackend throws unexpected exception

The module definition
var module = angular.module('test', []);
module.provider('client', function() {
this.$get = function($http) {
return {
foo: function() {
return $http.get('foo');
}
}
}
});
module.factory('service', ['client', function(client) {
return {
bar: function() {
return client.foo();
}
}
}]);
Basically, client is a wrapper for http calls, and service is a wrapper around the client basic features.
I'm unit testing both the provider and the service with karma+jasmine. The provider tests run as expected, but i have a problem with the service tests:
describe('service test', function(){
var service = null;
beforeEach(function(){
module('test')
inject(function(_service_, $httpBackend, $injector) {
service = _service_;
$httpBackend = $injector.get('$httpBackend');
});
});
it('should invoke client.foo via service.bar', function() {
$httpBackend.expect("GET", "foo");
service.bar();
expect($httpBackend.flush).not.toThrow();
});
});
I get Expected function not to throw, but it threw Error: No pending request to flush !.. When testing the provider with the same way, this test passes. Why?
When you are testing your service, you need to mock the client and inject that mock instead of the real client. Your mock can be in the same file if you only expect to use it for testing this service or in a separate file if you'll use it again elsewhere. Doing it this way does not require the use of $httpBackend (because you are not actually making an http call) but does require using a scope to resolve the promise.
The mock client:
angular.module('mocks.clientMock', [])
.factory('client', ['$q', function($q) {
var mock = {
foo: function() {
var defOjb = $q.defer();
defOjb.resolve({'your data':'1a'});
return defOjb.promise;
}
};
return mock;
}]);
Using the mock:
describe('service test', function(){
var service, mock, scope;
beforeEach(function(){
module('test', 'mocks.clientMock');
inject(function(_service_, $rootScope) {
service = _service_;
scope = $rootScope.$new();
});
});
it('should invoke client.foo via service.bar', function() {
spyOn(client, 'foo').and.callThrough();
service.bar();
scope.$digest();
expect(client.foo).toHaveBeenCalled();
});
});

writing jasmine unit test for angular factory

I am trying to test an angular factory using jasmine and mocha however I keep getting undefined errors.
here is my factory:
(function() {
'use strict';
angular.module('app').factory('appFactory', appFactory);
appFactory.$inject = ['$http','$q'];
function appFactory($http,$q) {
return {
get: get
};
function get(someData) {
//....
}
}
})();
and here is my unit test using jasmine and mocha:
'use strict';
describe('Factory: appFactory', function() {
var http, appFactory;
var $rootScope, $scope;
beforeEach(function() {
angular.module('app');
});
beforeEach(inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
}));
beforeEach(inject(function() {
var $injector = angular.injector(['ngMock', 'ng', 'app']);
appFactory = $injector.get('appFactory');
}));
it('should be defined', inject( function() {
alert(appFactory);
}));
});
so my alert looks something like:
Object{get: function get(someData) {...}
how do I go about testing the functionality of my factory if this is returning an object?

Jasmine: mocking Bower library

I am trying to create a mock for testing a service that depends on another one managed by bower. The code for the Jasmine test is the following (full example at plunker):
describe('jsonrpc', function() {
'use strict';
var uuidMock, $httpBackend, jsonrpc;
beforeEach(module('jsonrpc', function ($provide) {
uuidMock = {};
uuidMock.generate = function () { return 0; };
$provide.value('uuid', uuidMock);
}));
beforeEach(inject(function(_jsonrpc_, _$httpBackend_) {
jsonrpc = _jsonrpc_;
$httpBackend = _$httpBackend_;
}));
it('should have created $httpBackend', function() {
expect($httpBackend.get).toBeDefined();
});
});
The 'jsonrpc' service provider is defined as follows:
angular.module('jsonrpc', ['uuid']).provider('jsonrpc', function() {
'use strict';
var defaults = this.defaults = {};
defaults.basePath = '/rpc';
this.$get = ['$http', 'uuid4', function($http, uuid4) {
function jsonrpc(options, config) {
... (etc) ...
When I try to mock the dependency of the 'jsonrpc' module on the 'uuid' module, I get the following error:
$injector:modulerr http://errors.angularjs.org/1.2.16/$injector/modulerr?p0=jsonrpc&p1=%5B%24injector%3Amodulerr%5D%20http%3A%2F%2Ferrors.angularjs.org%2F1.2.16%2F%24injector%2Fmodulerr%3Fp0%3Duuid%26p1%3D%255B%2524injector%253Anomod
What am I doing wrong when it comes to mock up that dependency?
What you're doing is not right because you're modifying the provider of the jsrpc module, not the uuid module, and you're only calling $provide.value to provide what should be a whole module (not a value)
If uuid4 is the only part of uuid that you need to mock, you can do
module('jsrpc', function($provide) {
$provide.service('uuid4', uuid4Mock)
});
Where uuid4Mock provides the behaviour only of that service, or whatever it is in there.

$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

Categories