I have a module
export default angular.module('pfui.user', [])
.controller('ModifyUserController', ModifyUserController)
that has a controller
export default class ModifyUserController{
userid:string;
...
}
I'm trying to create a unit test that can test some methods in the controller that calls services to do some operation. This is my Karma script -
describe('ModifyUserControllerTester', function () {
var $controller;
beforeEach(angular.mock.module('ui.router'));
beforeEach(angular.mock.module('pfui.user'));
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('Test', function () {
it('test accessing controller', function () {
let $scope = {};
var controller = $controller('ModifyUserController', {
$scope: $scope
});
expect($scope['userid']).toBe(undefined);
});
});
});
When I run the test, I get an error
Error: [$injector:unpr] Unknown provider: UsersProvider <- Users <- ModifyUserController
Initially I was getting an error that $stateProvider was missing. So I added
beforeEach(angular.mock.module('ui.router'));
and that error went away.
This is my first attempt in writing a Karma test. I'm not sure what I am missing. Why is Karma looking for a Provider when I don't have one in the module? Any help is greatly appreciated.
Your question doesn't show any dependency injections to the ModifyUserController but going by the error you have posted it looks like you haven't provided the 'Users' Service to the controller.
describe('ModifyUserControllerTester', function () {
var $controller;
var mockUsers;
beforeEach(angular.mock.module('ui.router'));
beforeEach(angular.mock.module('pfui.user'));
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('Test', function () {
it('test accessing controller', function () {
//----define your mock dependency here---//
let mockUsers = jasmine.createSpyObj('mockUsers', ['user_method1',
'user_method2',...]);
let $scope = {};
var controller = $controller('ModifyUserController', {
$scope: $scope,
Users: mockUsers
});
expect($scope['userid']).toBe(undefined);
});
});
});
PS. Since its best practice for unit tests to be conducted in isolation, you should also consider providing a mock state provider vs importing the actual ui.router module
Related
Module is defined as
var $manage = angular.module('manage', [....]);
Controller is defined as
$manage.line.events.controller('eventsController', [..., function(...){
$scope.page = "events";
}]);
My simple unit test case is
describe('Module: manage', function() {
beforeEach(module('manage'));
var scope, ctrl, rootScope;
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('eventsController', {
$scope: scope
});
}));
it("test page", function () {
expect(scope.page).toEqual('events');
});
});
Here, i am getting a error like
Failed to instantiate module ampleManage due to.... manage is not available.
I have integrated angular-mocks.js also. Tried so many possibilities but not working for me.
Basic need is .
Need to access controller/scope in test case.
I'm new to the unit testing in the client side. My application uses the express.js, angularjs-ui-router and node.js. Currently i start writing the unit test cases for the application. I'm using Karma, Mocha, Chai, Sinon for unit testing.
My router config look like below:
$stateProvider
.state('drive', {
url: '/drive',
templateUrl: 'drive.jade',
controller: 'driveCtrl',
});
Controller:
angular.module('mApp').controller('driveCtrl', ['$scope', 'driveService',
function($scope, driveService) {
var driveInfo = driveService.get({}, function() {});
driveInfo.$promise.then(function(rs) {
var drivers = [];
//Logical operation
$scope.drivers = drivers;
});
}]);
Factory Resource:
mApp.factory('driveService', ['$resource', function($resource) {
return $resource('/drive/id/:id/', {id:'#id'});
}]);
The driveService is a factory which insides uses a angular.js $resources. I tried variety of options but nothings seems to be working (using the $httpbackend, $q). Can you help me out to write the way to test the controller by mocking the driveService.
Here's my unit test code:
var expect = require('chai').expect;
var sinon = require('sinon');
describe('Drive Service initialisation', function() {
var scope, controller, state, $q, mockDriveService, driveServiceResponse = [{name:'james', type: 'heavy'}], queryDeferred;
beforeEach(angular.mock.module('mApp'));
angular.mock.module(function($provide){
$provide.value('driveService', mockDriveService);
});
describe(' drive service called', function() {
beforeEach(inject(function($controller, $rootScope, $state, _$q_, driveService) {
$q = _$q_;
scope = $rootScope.$new();
mockDriveService = {
get:function(){
queryDeferred = $q.defer();
queryDeferred.resolve(driveServiceResponse);
return {$promise: queryDeferred.promise};
}
};
controller = $controller('driveCtrl', { $scope: scope, driveService:driveService});
sinon.stub(mockDriveService, 'get');
scope.$apply();
}));
it('expect filter to be empty', function () {
expect(scope.drivers).to.not.be.empty;
});
});
});
The error what i'm getting is:
Error: Unexpected request: GET /driveService/id
No more request expected
at $httpBackend (node_modules/angular-mocks/angular-mocks.js:1210:9)
at sendReq (public/javascripts/lib/angular/angular.js:10333:9)
at serverRequest (public/javascripts/lib/angular/angular.js:10045:16)
at processQueue (public/javascripts/lib/angular/angular.js:14567:28)
at public/javascripts/lib/angular/angular.js:14583:27
at Scope.$eval (public/javascripts/lib/angular/angular.js:15846:28)
at Scope.$digest (public/javascripts/lib/angular/angular.js:15657:31)
at Scope.$apply (public/javascripts/lib/angular/angular.js:15951:24)
at Context.<anonymous> (C:/Users/por/AppData/Local/Temp/b0475694b46e0d60262621ad126ce46c.browserify:63:9)
at Object.invoke (public/javascripts/lib/angular/angular.js:4450:17)
Error: Declaration Location
at window.inject.angular.mock.inject (node_modules/angular-mocks/angular-mocks.js:2375:25)
You're defining a mocked service but still providing unmocked driveService for controller (this has been already done by default).
The fact that driveService.get is stubbed after the controller was instantiated and the method was called, doesn't help the case.
It should be something like
mockDriveService = {
get: sinon.stub().returns({$promise: $q.resolve(driveServiceResponse)})
};
controller = $controller('driveCtrl', { $scope: scope, driveService:mockDriveService});
scope.$apply();
The app should have test-friendly design to be tested efficiently. Considering that router is loaded in top-level module and its configuration is defined there too, app units that are supposed to be tested should be defined in child modules, so they could be tested in isolation.
The app may be refactored as
angular.module('mApp', ['ui.router', 'mApp.drive']);
...
angular.module('mApp.drive', [])
.controller('driveCtrl', ...)
.factory('driveService', ...);
And its units may be tested like
beforeEach(angular.mock.module('mApp.drive'));
...
I am running my tests with karma and phantom, Also I'm using mocha and sinon and tests are getting failed with below error:
EditResourceCategoryDialogTest EditResourceCategoryDialogController "before each" hook: workFn
Error: [$injector:modulerr] http://errors.angularjs.org/1.4.9/$injector/modulerr?p0=resourceofferingsApp&p1=Error%3A%20%5B%24injector%3Amodulerr%5D%20
Sample code:
define(function (require) {
"use strict";
var assert = require('chai').assert;
var sinon = require('sinon');
var angular = require('angular');
var angularMocks = require('angular.mocks');
require('resourceofferings/app');
require('dialog path');
describe('EditResourceCategoryDialogTest', function () {
beforeEach(module('resourceofferingsApp'));
describe('EditResourceCategoryDialogController', function () {
var $scope, ctrl;
//you need to inject dependencies first
beforeEach(inject(function ($rootScope, $injector) {
$scope = $rootScope.$new();
}));
it('initialization test (create mode)', inject(function ($controller) {
ctrl = $controller("EditResourceCategoryDialogController", {
$scope: $scope,
$uibModalInstance: null,
options: {
isEditMode: false
}
});
assert.equal($scope.isEditMode, false);
}));
});
});
});
Its exactly getting failed here:
beforeEach(inject(function ($rootScope, $injector) {
$scope = $rootScope.$new();
}));
Please help me to fix this issue..
Thanks in advance.
Try this ...
describe('controllers', function(){
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new(); // this is what you missed out
controller = $controller('EditResourceCategoryDialogController', {
$scope: scope,
$uibModalInstance: null,
options: {
isEditMode: false
}
});
}));
});
Update: According to Angular ...
A common reason why the module fails to load is that you've forgotten
to include the file with the defined module or that the file couldn't
be loaded.
Are you sure all needed files are loaded?
I keep running into problems at the minute when i'm testing my AngularJS applications, I try and inject all dependencies however it doesn't seem to be working, any help is greatly appreciated :)
It's quite a large application and i'm trying to break things down as much as possible and test them, however we have a factory called firebaseUser which is, as you can guess a firebaseUser. We also have an instance of this known as userInstance so I'm getting errors whenever I try and mock userInstance.
describe('Dashboard Start Controller', function () {
var scope, ctrl;
beforeEach(function () {
MockFirebase.override();
module('noodleApp.start');
module('noodleApp.noodleFactory');
module('noodleApp.firebaseUser');
module('noodleApp.start');
});
beforeEach(inject(function($rootScope, $controller, $injector) {
scope = $rootScope.$new();
ctrl = $controller('StartController', {$scope: scope});
}));
afterEach(function () {
scope.$destroy();
});
it('should be available', function() {
expect(ctrl).toBeDefined();
});
it('should init with filter being set to all', function() {
expect(scope.filterOn).toBe('all');
});
});
Whenever I run this test I get the following error: Unknown provider: userInstanceProvider <- userInstance <- StartController
$controller is a call to $inject but treated as a service. What happens is you are instantiating the controller but because $inject is used, and you are not passing userInstance, it looks for a provider which isn't found. You need to make sure to pass the service/factory/resolve to your $controller method. By mocking it out and passing it to the controller, you can isolate what you expect to happen and only test the controller in this unit test.
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
userInstanceMock = {
// mock out all methods here
foo: sinon.stub()
}
ctrl = $controller('StartController', {
$scope: scope,
userInstace: userInstaceMock
});
}));
My Jasmine unit test is as follows
describe('controllers', function () {
'use strict';
beforeEach(module('myapp.controllers'));
angular.mock.module('myapp.controllers', function ($provide) {
$provide.value('$localStorage', $localStorage);
});
it('should have a creationController', inject(function ($controller, _$rootScope_, localize) {
var scope = _$rootScope_.$new();
var localize = localize;
var myCtrl1 = $controller('creationController', {
$scope: scope,
localize: localize
});
expect(myCtrl1).toBeDefined();
}));
});
When I try to execute the test I'm getting the following error
Error: [$injector:unpr] http://errors.angularjs.org/1.2.20/$injector/unpr?p0=localizeProvider%20%3C-%20localize
There is a dependency called as "localize" being used in the controller. However I'm unable to inject that in to this unit test.
Any idea how I can solve this problem ?
injector look for _componenrToBeInjected_ for injection . If you have a service name localized (Are you sure you have one ?) then for injection use
_ localize_ so your it block should look like this .
it('should have a creationController', inject(function ($controller, _$rootScope_, _localize_) {
var scope = _$rootScope_.$new();
var localize = _localize_;
var myCtrl1 = $controller('creationController', {
$scope: scope,
localize: localize
});
expect(myCtrl1).toBeDefined();
}));