I'm trying unit testing with Jasmine and Karma, but for some reason my Angular modules cannot be found. I've modified code from examples:
karma.config.js:
files: [
'lib/angular.js',
'lib/angular-mocks.js',
'js/app.js',
'js/controllers.js',
'js/factories.js',
'tests/**/*.js'
]
app.js:
var app = angular.module('app', ['ngRoute']);
app.config(function ($routeProvider) {
$routeProvider
.when('/test', {
templateUrl: 'views/test.html',
controller: 'TestCtrl'
})
.otherwise({redirectTo: '/'});
});
controllers.js:
app.controller('TestCtrl', function ($scope, $location) {
console.log('Test Controller');
$scope.isActive = function(route) {
return route === $location.path();
};
});
test-spec.js:
describe('TestCtrl testing', function () {
var scope, $location, createController;
beforeEach(inject(function ($rootScope, $controller, _$location_) {
$location = _$location_;
scope = $rootScope.$new();
createController = function () {
return $controller('TestCtrl', {
'$scope': scope
});
};
}));
it('should...', function () {
var controller = createController();
$location.path('/test');
expect($location.path()).toBe('/test');
expect(scope.isActive('/test')).toBe(true);
expect(scope.isActive('/contact')).toBe(false);
});
});
error message:
Error: [ng:areq] Argument 'TestCtrl' is not a function, got undefined
I also tried with: beforeEach(module('TestCtrl')), but it did not help.
What have I missed?
There are two problems I can see. In describe section there must be main module injection:
beforeEach(module('app'));
The second problem is that you forgot to add ngAnimate module to Karma files config array:
files: [
'lib/angular.js',
'lib/angular-route.js', // <-- this guy
'lib/angular-mocks.js',
...
]
Related
I am trying to make a small test work that validates wether the controller is defined.
The error I am receiving is:
myApp.orders module Order controller should .... FAILED
Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope <- OrdersCtrl
Reading similar errors it has something to do with the dependencies, but I don't know what's wrong.
Controller:
'use strict';
angular.module('myApp.orders', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/orders', {
templateUrl: 'orders/orders.template.html',
controller: 'OrdersCtrl'
});
}])
.controller('OrdersCtrl', function($scope, $location) {
$scope.changeView = function(view){
$location.path(view); // path not hash
}
});
Test:
'use strict';
describe('myApp.orders module', function() {
beforeEach(module('myApp.orders'));
describe('Order controller', function(){
it('should ....', inject(function($controller) {
//spec body
var OrdersCtrl = $controller('OrdersCtrl');
expect(OrdersCtrl).toBeDefined();
}));
});
});
This is because you are not passing the $scope variabl einside the controller when you are creating it in test. And the controller tries to define $scope.changeView, but it finds $scope as undefined.
You need to pass a $scope variable to the controller in your test.
var $rootScope, $scope, $controller;
beforeEach(function() {
module('myApp.orders');
inject(function (_$rootScope_, _$controller_) {
$rootScope = _$rootScope_;
$scope = _$rootScope_.$new();
$controller = _$controller_;
});
});
and in your test,
var OrdersCtrl = $controller('OrdersCtrl', { $scope: $scope });
Restructure your unit test slightly. We have a pattern where the controller is defined in the beforeEach() so it's ready for the test. You also need to import the controller you're testing:
import ControllerToTest from 'path/to/your/real/controller';
describe('myApp.orders module',() => {
let vm;
beforeEach(() => {
inject(($controller, $rootScope) => {
vm = $controller(ControllerToTest,
{
$scope: $rootScope.$new()
};
});
});
describe('Order Controller', () => {
it('should do something', () => {
expect(vm).toBeDefined();
});
});
});
Chaneg your controller like this
.controller('OrdersCtrl',['$scope', '$location', function($scope, $location) {
$scope.changeView = function(view){
$location.path(view); // path not hash
}
}]);
I've been trying to test my controller:
app.js
angular
.module('MyModule', [
'ui.router'
]);
angular
.module('MyModule')
.config(configFn);
configFn.$inject = ['$stateProvider'];
function configFn($stateProvider){
$stateProvider
.state('myState',{
url:'state',
views: {
'main' : {
templateUrl: 'src/views/view.html',
controller: 'MyCtrl',
controllerAs: 'ctrl',
resolve: {
DataResolve: ['MyService', function(MyService){
return MyService.getData();
}]
}
}
}
});
controller.js
angular
.module('MyModule')
.controller('MyCtrl', Controller);
Controller.$inject = ['DataResolve'];
/* #ngInject */
function Controller(DataResolve) {
var vm = this;
vm.data = DataResolve;
}
My spec
controller_spec.js
describe('Controller', function(){
beforeEach(module('MyModule'));
beforeEach(inject(function($controller){
this.myCtrl = $controller('MyCtrl');
}));
it('Controller should be defined', function() {
expect(this.myCtrl).toBeDefined();
});
});
But when the test runs, I get the following error:
Error: [$injector:unpr] Unknown provider: DataResolveProvider <- DataResolve <- MyCtrl
What I have been doing wrong?
In your beforeEach, add a reference to your service :
beforeEach(inject(function($controller, DataResolve){
this.DataResolve = DataResolve;
this.myCtrl = $controller('ParcelasController', {
DataResolve: this.DataResolve;
});
}));
following is the code from my sample angular project.
app.js code:
(function () {
'use strict';
var app = angular.module('actorsDetails', [
// Angular modules
'ngResource',
// 3rd Party Modules
'ui.bootstrap',
'ui.router'
]);
app.config(['$stateProvider', '$urlRouterProvider', configRoutes]);
function configRoutes($stateProvider, $urlRouterProvider) {
$stateProvider
.state('main', {
url: '/main',
templateUrl: 'app/home/home.html',
controller: 'HomeCtrl',
controllerAs: 'vm'
})
.state('form', {
url: '/form',
templateUrl: 'app/form/form.html',
controller: 'FormCtrl',
controllerAs: 'vm',
resolve: {
initialData: ['actorApi', function (actorApi) {
return actorApi.getActorsResource();
}]
}
})
.state('resource', {
url: '/resource',
templateUrl: 'app/resource/resource.html',
controller: 'ResourceCtrl',
controllerAs: 'vm',
resolve: {
initialData: ['actorApi', function (actorApi) {
return actorApi.getActorsResource();
}]
}
});
$urlRouterProvider.otherwise('/main');
}
app.run(['$state', function ($state) {
// Include $route to kick start the router.
}]);
})();
controller code:
(function () {
'use strict';
angular.module('actorsDetails').controller('HomeCtrl', HomeCtrl);
/* #ngInject */
function HomeCtrl($state) {
/* jshint validthis: true */
var vm = this;
vm.activate = activate;
vm.test = true;
vm.navigate = navigate;
activate();
function activate() {
}
function navigate() {
$state.go('form');
}
}
})();
**test.js**
describe('HomeCtrl', function() {
beforeEach(module('actorsDetails'));
beforeEach(inject(function ($rootScope, $controller) {
var scope = $rootScope.$new();
var HomeCtrl = $controller('HomeCtrl', {
$scope: scope
});
}));
it('should have a HomeCtrl controller', function() {
expect(true).toBeDefined();
});
});
there are the files I have included in my karma.config.js
I have added all the angularJS dependent files.
I have also added the controller file that i need to test
files: [
'src/lib/angular/angular.min.js',
'src/lib/angular-mocks/angular-mocks.js',
'src/lib/angular-resource/angular-resource.min.js',
'src/lib/angular-route/angular-route.min.js',
'src/app/app.js',
'src/app/home/home.controller.js',
'src/test/specs/*.js'
],
kindly pinpoint me, what is that I am doing wrong...
Mock out the $state object in your unit test.
var mockState = { go: function() {} };
var HomeCtrl = $controller('HomeCtrl', { $scope: scope, $state: mockState });
The $injector:modulerr is most likely related to your use of $state in your controller. Instead of mocking, you could try adding the library to your karma config, and loading the module in your unit test.
'src/lib/angular-ui-router/release/angular-ui-router.min.js'
beforeEach(module('ui.router'));
I am having an issue with testing a routeProvider. There seems to be a lot of information out there, but everything I do, I just cannot get the unit test to digest the location. I was hoping to find some closure here. I am feeling my way through angularJS and unit testing, and could use any advice.
Here is the routeProvider function:
use strict';
var myApp = angular.module('myApp', [
'ngRoute',
'myServices',
'myControllers'
]);
myApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('fooBar', {
templateUrl: 'barFoo',
controller: 'SummaryCtrl'
}).
otherwise({
redirectTo: 'defaultFooBar'
});
}]);
I am trying to test this routeProvider with the following, but I cannot, for the life of my, inject the change I make to location.
'use strict';
describe('myApp',function() {
var $scope;
var location;
beforeEach(inject(function($rootScope, $location) {
$scope = $rootScope;
location = $location;
}));
it('redirect to default', function() {
location.path('efipoejs');
$scope.$apply();
expect(location.path()).toBe('defaultFooBar');
})
});
Ive tried $scope.$digest, and .$apply.... what ever I do, I always get this outcome:
Expected '/efipoejs' to be 'defaultFooBar'.
I really want angularJS to work... If you have any advice that might lead me down the right path...I would seriously appreciate it.
ANSWER: BECAUSE I CANT POST AN ANSWER WITHIN 8 HOURS!!!!
yo yea,...
Here's how I got this to work
describe('Testing routes', function() {
beforeEach(module('myApp'));
var location, route, rootScope;
beforeEach(inject(
function( $location, $route, $rootScope ) {
location = $location;
route = $route;
rootScope = $rootScope;
}));
describe('Default Routing', function() {
beforeEach(inject(
function($httpBackend) {
$httpBackend.expectGET('DefaultUrl').respond(200);
}));
it('should load the default page on fake path', function() {
location.path('/ermagerd');
rootScope.$digest();
expect(location.path()).toBe('defaultFooBar');
});
});
});
});
Once again, here is my logic
'use strict';
var myApp = angular.module('myApp', [
'ngRoute',
'myServices',
'myControllers'
]);
myApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('fooBarDefault', {
templateUrl: 'defaultUrl',
controller: 'DefaultCtrl'
}).
otherwise({
redirectTo: 'fooBarDefault'
});
}]);
I have this controller:
angular.module('clientApp')
.controller('MainCtrl', function ($scope, projects) {
$scope.projects = projects;
});
projects is a resolve from a database. It works in the view.
This is my service:
angular.module('clientApp.services', ['ngResource'])
.factory('Projects', function($resource){
return $resource('/api/project/:prj_id', {'prj_id':'#prj_id'});
})
.factory('MultiProjectsLoader',['Projects', '$q', '$stateParams',
function(Projects, $q) {
return function() {
var delay = $q.defer();
Projects.query(function(projects) {
delay.resolve(projects);
}, function() {
delay.reject('Unable to fetch sizes');
});
return delay.promise;
};
}
]);
And this is my app.js
$stateProvider
.state('home', {
url: '/',
templateUrl: 'views/home.html',
resolve:{
projects: ['MultiProjectsLoader', function(MultiProjectsLoader){
return new MultiProjectsLoader();
}]
},
controller: 'MainCtrl'
});
Trying to write a test for this:
'use strict';
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('clientApp'));
beforeEach(function () {
angular.module('clientApp.services');
});
var MainCtrl,
scope;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
MainCtrl = $controller('MainCtrl', {
$scope: scope
});
}));
it('should attach a list of projects to the scope', function () {
expect(scope.projects.length).toBeGreaterThan(1);
});
});
I get:
Error: [$injector:unpr] Unknown provider: projectsProvider <- projects
I guess I need to include the service somehow beforeEach(..). But I can't get it working. Any ideas?
You can inject the service a couple ways but my recommended way is to mock the service.
describe('Controller: MainCtrl', function () {
var
projectServiceMock = {
getData: function() {}
},
DATA_FROM_SERVER = {}
;
// load the controller's module
beforeEach(module('clientApp'));
beforeEach(function () {
angular.module('clientApp.services');
//angular.module('project'); // But dont use this method as you will be testing the service in the controller:
});
var MainCtrl,
scope;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
spyOn(projectServiceMock, 'getData').andReturn(DATA_FROM_SERVER);
MainCtrl = $controller('MainCtrl', {
$scope: scope,
project: projectServiceMock
});
}));
it('should attach a list of projects to the scope', function () {
expect(projectServiceMock.getData).toHaveBeenCalledWith(DATA_FROM_SERVER);
expect(scope.projects.length).toBeGreaterThan(1);
});
});
Your service should expose a method for returning the data it has gotten from the server not just straight data through project.
For example:
project.getData();