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();
});
});
});
Related
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
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) {
})
I decided to learn how to test my angular code with Jasmine. Everything works when I don't use specific dependencies but if there are some dependencies I have problems. For example we have controllers.js:
angular.module('myApp', ['jmdobry.angular-cache'])
.controller('MyCtrl', ['$scope', '$http', function($scope, $http) {
$scope.myName = "Wojtek";
/...
}]);
And now I want to test:
describe('myApp', function() {
var scope,
controller;
beforeEach(angular.mock.module('myApp'));
describe('MyCtrl', function() {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope
});
}));
it('sets proper name', function () {
expect(scope.myName).toBe("Wojtek");
});
});
});
My question is - how to mock that 'jmdobry.angular-cache' dependency?
Since you don't need the actual mocking functionality for that module in your tests you can do something like this:
describe('myApp', function () {
var scope,
controller;
beforeEach(angular.mock.module('jmdobry.angular-cache', [])); // just create a module that does nothing
beforeEach(angular.mock.module('myApp'));
describe('MyCtrl', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope
});
}));
it('sets proper name', function () {
expect(scope.myName).toBe("Wojtek");
});
});
});
Why am I unable to access my function in my Controller? The code functions like I would expect it too, however, it doesn't seem to want to allow me access to my function that I'm trying to unit test. It should just return a simple bool, but it's getting killed somewhere.
Here's some code:
RTHelper.js
describe('Unit: LocationController', function () {
var $scope, $httpBackend, $location, injector, ctrl, $controller;
//beforeEach(function () {
// angular.module('TDE').controller('LocationController'); //
// inject(function ($injector) {
// $rootScope = $injector.get('$rootScope');
// $scope = $rootScope.$new();
// //ctrl = $injector.get('$controller')("LocationController", { $scope: $scope });
// injector = $injector;
// ctrl = $injector.get('$controller');
// //scope = $injector.get('$rootScope').$new();
// $httpBackend = $injector.get('$httpBackend');
// $location = $injector.get('$location');
// });
//});
//both beforeEach methods work(which one is better? I don't know), so things are getting loaded
beforeEach(function () {
angular.module('TDE');
inject(function ($injector) {
$location = $injector.get('$location');
$rootscope = $injector.get('$rootScope');
$scope = $rootscope.$new();
$controller = $injector.get('$controller');
ctrl = function () {
return $controller('LocationController', {
'$scope': $scope
})
};
})
});
it("should just be a holder for something for later", function () {
expect($scope.BoolCondition()).toBeDefined(); //I don't care what it returns as long as it's accessed honestly
});
})
LocationController.js
angular
.module('TDE')
.controller('LocationController', ['$rootScope', '$scope', '$location', '$window', '$document', 'LocationService', 'HeaderFooterService', 'SearchService', 'TranslationService', 'MTDE_CONFIG', 'LocationPartnerAssignmentService', 'ExperimentService', function ($rootScope, $scope, $location, $window, $document, $LocationService, $HeaderFooterService, $SearchService, $TranslationService, $MTDE_CONFIG, $LocationPartnerAssignmentService, $ExperimentService) {
$scope.BoolCondition = function(myCondition){
if(//blah blah condition test on myCondition)
{
return true
}
else
{
return false;
}
}
How would I go about getting to that BoolCondition? I'm new to this so you can imagine the struggle of writing unit tests after never having done unit testing. I've also gone through countless examples and I've done some generic tests, so I'm not totally un-versed.
You're not bootstrapping the module under test correctly. You should use angular.mock.module inside the test
You're not instantiating the controller (where's the call to ctrl()?)
Here's the complete working example and the fiddle that runs it:
describe('Unit: LocationController', function () {
var $scope, $location, ctrl;
beforeEach(function () {
angular.mock.module('TDE');
inject(function (_$location_, _$rootScope_, _$controller_) {
$location = _$location_;
$scope = _$rootScope_.$new();
ctrl = _$controller_('LocationController', {
'$scope': $scope
})
});
});
it("should just be a holder for something for later", function () {
expect($scope.BoolCondition()).toBeDefined();
expect($scope.BoolCondition()).toBeTruthy();
});
})
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!