Unit test controller, angularJS and jasmine - javascript

Hi I've been trying to unit test basic functions in my controller however I can't seem to connect when setting up the unit test.
Error: [$injector:modulerr] Failed to instantiate module myApp due to:
[$injector:nomod] Module 'myApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
Here is my controller:
var myApp = angular.module("myApp", []);
myApp.controller('studentController', function($scope,$route,$routeParams,$http,$location){
//Get all students
$scope.getStudents = function(){
$http.get('/api/student/').then(function(response){
$scope.students = response.data;
});
};
and my test:
describe("studentController", function () {
beforeEach(module('myApp'));
var $controller;
beforeEach(inject(function (_$controller_){
$controller = _$controller_;
}))
describe("studentController", function(){
it("should get student data", function (){
var $scope = {};
$scope.getStudents();
expect($scope.students.length).toBe(15)
})
})
});
I have included both these files in the jasmine html file along with the angular-mocks.js
any help would be much appreciated

You are injecting $route, but you are not loading the ngRoute module. Load the file angular-route.js and state the dependency:
var myApp = angular.module("myApp", ['ngRoute']);

You have to create controller in before each by using following and as you are calling getStudents that shuold be mocked by using HttpBackend service in unit test.
Controller
var myApp = angular.module("myApp", []);
myApp.controller('studentController', function($scope, $route, $routeParams, $http, $location) {
//Get all students
$scope.getStudents = function() {
$http.get('/api/student/').then(function(response) {
$scope.students = response.data;
});
};
});
Test file
describe("studentController", function() {
beforeEach(module('myApp'));
var $controller, scope, route;
beforeEach(inject(function(_$controller_, $rootScope, $route, $routeParams, $http, $location) {
$controller = _$controller_;
scope = $rootScope.$new();
$controller('studentController', {
'$scope': scope,
'$route': $route,
'$routeParams': $routeParams,
'$http': $http,
'$location': $location
});
}))
describe("studentController", function() {
it("should get student data", function() {
// for this you have to use httpBackend
// you have to mock the response of api
$scope.getStudents();
// then you are able to verify the result in student
expect($scope.students.length).toBe(15)
})
})
});
For more information you can refer unit testing and Httpbackend

Related

karma test controller using toBeDefined failed

My test failed because it says my controller is not defined. So strange I think I did everything right.
describe('homeCtrl', function() {
var httpBackend, controller, scope;
beforeEach(module('App'));
beforeEach(inject(function($httpBackend, $controller) {
scope = {};
httpBackend = $httpBackend;
controller = $controller('homeCtrl', { $scope: scope });
}));
it('should exist', function() {
expect(controller).toBeDefined();
});
});
and I have my home.js which is the controller like this
var App = angular.module('App')
App.controller('homeCtrl', function($scope) {
})
The error is Expected undefined to be defined.
Your home.js should have dependencies injected in module, change it as,
var App = angular.module('App',[])
App.controller('homeCtrl', function($scope) {
})

How to test $http with Jasmine in AngularJS

I'm trying to test the data received from an $http request in my controller.
I don't have too much experience testing with Angular so I'm struggling to under stand how to do it.
$scope. always comes back undefined and when I've tried fetching the data from the test, that seems to fail also. What am I missing?
Controller:
'use strict';
var myApp = angular.module('myApp.view1', ['ngRoute']);
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'
});
}]);
myApp.controller('View1Ctrl', [
'$scope',
'$http',
function($scope, $http) {
$http.get('view1/data.json')
.then(function(res){
$scope.data = res.data.data
});
}]);
Test:
'use strict';
describe('myApp.view1 module', function() {
beforeEach(module('myApp.view1'));
describe('view1 controller', function(){
var scope, testCont;
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
testCont = $controller('View1Ctrl', {$scope: scope});
}));
it('should....', function(){
expect($scope.data).toBeDefined();
});
});
});
The HTTP requests will not fire unless you call $httpBackend.flush().
More information can be found here: http://docs.angularjs.org/api/ngMock.$httpBackend
Test:
'use strict';
describe('myApp.view1 module', function() {
var $httpBackend, $rootScope, createController, jsonHandler;
beforeEach(module('myApp.view1'));
describe('view1 controller', function(){
var scope, testCont;
beforeEach(inject(function($rootScope, $controller, $injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// backend definition common for all tests
jsonHandler= $httpBackend.when('GET', '/view1/data.json')
.respond({data: '[XXX,XXX,XXX]'});
// Get hold of a scope (i.e. the root scope)
$rootScope = $injector.get('$rootScope');
// The $controller service is used to create instances of controllers
var $controller = $injector.get('$controller');
createController = function() {
return $controller('View1Ctrl', {'$scope' : $rootScope });
};
}));
it('should....', function(){
$httpBackend.expectGET('/view1/data.json');
var controller = createController();
$httpBackend.flush();
});
});
});

AngularJS: '$scope is not defined'

I keep getting '$scope is not defined' console errors for this controller code in AngularJS:
angular.module('articles').controller('ArticlesController', ['$scope', '$routeParams', '$location', 'Authentication', 'Articles',
function($scope, $routeParams, $location, Authentication, Articles){
$scope.authentication = Authentication;
}
]);
$scope.create = function() { // THROWS ERROR ON THIS INSTANCE OF $SCOPE
var article = new Articles({
title: this.title,
content: this.content
});
article.$save(function(response) {
$location.path('articles/' + response._id);
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
Where in my AngularJS MVC files should I be looking at to find problems with the $scope not being defined properly?
For others who land here from Google, you'll get this error if you forget the quotes around $scope when you're annotating the function for minification.
Error
app.controller('myCtrl', [$scope, function($scope) {
...
}]);
Happy Angular
app.controller('myCtrl', ['$scope', function($scope) {
...
}]);
Place that code inside controller:-
angular.module('articles').controller('ArticlesController', ['$scope', '$routeParams', '$location', 'Authentication', 'Articles',
function($scope, $routeParams, $location, Authentication, Articles){
$scope.authentication = Authentication;
$scope.create = function() { // THROWS ERROR ON THIS INSTANCE OF $SCOPE
var article = new Articles({
title: this.title,
content: this.content
});
article.$save(function(response) {
$location.path('articles/' + response._id);
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
}
]);
Just put you $scope.create function inside your controller. Not outside !
$scope is only defined in controllers, each controller have its own. So write $scope outside your controller can't work.
Check scope variable declared after controller defined.
Eg:
var app = angular.module('myApp','');
app.controller('customersCtrl', function($scope, $http) {
//define scope variable here.
});
Check defined range of controller in view page.
Eg:
<div ng-controller="mycontroller">
//scope variable used inside these blocks
<div>

How can I inject an Angular controller into my unit test

I'm really hoping this is a trivial issue. Yes, I have RTFM. I'm actually using the Angular documented way to inject a controller, but for some reason my controller is not defined. The main difference here is that I'm used to working on single module apps and this time I have a multi module app. I wouldn't think it would make a difference, but there you go. Instead of a lengthy description, I'll get straight to the code:
Using Angular 1.2.16
Unit Test Framework: Jasmine
app.js
angular.module('OBB', [
// Native AngularJS DI
'ngResource', 'ngCookies',
// bunch of modules
...
// OBB Page Modules
'OBB.home', 'OBB.buckets', 'OBB.company', 'OBB.advSearch', 'OBB.users'
])
So, I'm trying to test a controller in the OBB.home module.
home.js
angular.module('OBB.home', ['ui.router'])
.controller('HomeCtrl', ['$log', '$rootScope', '$scope', '$state', 'AUTH_EVENTS',
function HomeCtrl ($log, $rootScope, $scope, $state, AUTH_EVENTS) {
$scope.signInFormData = {
email: null, password: null
};
//more code...
}]);
home.spec.js
describe('Unit Home Controllers: ', function () {
var homeController, scope;
beforeEach(module('OBB.home'));
beforeEach(inject(function (_$rootScope_, $controller) {
scope = _$rootScope_.$new();
homeController = $controller('HomeCtrl', {
$rootScope: _$rootScope_,
$scope: scope,
$log: {},
$state: {},
AUTH_EVENTS: {},
});
}));
it('Home Controller is correctly instantiated', inject(function () {
expect(scope).toBeDefined(); // Pass
expect(scope.signInFormData).toBeDefined(); // Fails
}));
});
You need to mock/load your dependencies.
describe('Unit Home Controllers: ', function () {
var homeController, scope;
beforeEach(module('OBB.home'));
beforeEach(inject(function (_$rootScope_, $controller) {
scope = _$rootScope_.$new();
homeController = $controller('HomeCtrl', {
$rootScope: _$rootScope_,
$scope: scope,
$log: {}, //You will have to add methods as needed
$state: {},
AUTH_EVENTS: {}
});
}));
it('Home Controller is correctly instantiated', inject(function () {
expect(scope).toBeDefined(); // Fails
expect(scope.signInFormData).toBeDefined(); // Fails
}));
});

AngularJS Issues mocking httpGET request

so I'm new to angularjs and its mocking library. I am trying to test that a specific GET request is made, but I always get this error for the 2nd assertion and can't figure out why:
Error: Unsatisfied requests: GET /1.json
Is there anything I messed up with my code below?
App.js
var App = angular.module('App', []).config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
}).when('/Items', {
templateUrl: 'views/items.html',
controller: 'Ctrl'
}).otherwise({
redirectTo: '/'
});
}]);
Ctrl.js
function Ctrl($scope, $http, $filter) {
$scope.items = [];
$http.get('/1.json').success(function(data) {$scope.items = data.items;});
}
Ctrl.$inject = ["$scope","$http", "$filter"];
Spec/Ctrl.js
describe('Controller: Ctrl', function() {
var $httpBackend;
// load the controller's module
beforeEach(module('App'));
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
// backend definition common for all tests
$httpBackend.whenGET('/1.json').respond('Response!');
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
var Ctrl, scope;
// Initialize the controller and a mock scope
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
Ctrl = $controller('Ctrl', {
$scope: scope
});
}));
it('should initialize with 0 items', function() {
expect(scope.items.length).toBe(0);
$httpBackend.flush();
});
it('should make store request', function(){
var controller = scope.$new(Ctrl);
$httpBackend.expectGET('/1.json');
$httpBackend.flush();
});
});
EDIT: added app and controller code.
I finally got my unit tests working! Mostly because I restructured my application to make more sense and be more modular.
I'll try to give information to help the next person that runs into this:
first of was I switched to using the $resource instead of $http.
instead of injecting $injector, I injected $httpBackend like so:
beforeEach(inject(function(_$httpBackend_, $rootScope, $route, $controller){
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('/path/to/api').respond([{id:1}]);
instead of referencing 'Ctrl' as a string, I passed in the actual class
Ctrl = $controller('Ctrl', {
$scope: scope
});
became
var ProductsCtrl = ['$scope', function($scope){ ... }];
Ctrl = $controller(ProductsCtrl, {
$scope: scope
});`
Make sure you are referencing the angular-resources.js file if you are using $resources
I'm really loving Angularjs; I think it just takes some time to wrap your head around how to test. Best of luck out there!

Categories