I have the following test:
describe("An Angularjs test suite",function(){
beforeEach(module('myApp'));
var scope,controller;
beforeEach(inject(function($rootScope,$controller){
scope = $rootScope.$new(),
controller = $controller('homeController',{$scope:scope});
}));
it('should return true',function(){
expect(scope.helloWorld()).toBe('hello');
});
});
in the homecontroller i have this:
$scope.helloWorld = function(){
return 'hello';
};
test runs faulty and said 'undefined is not a function', what is the problem?
(function (angular) {
// Create module
var myApp = angular.module('myApp', []);
myApp.controller('homeController', function($scope){
$scope.helloWorld = function(){
return 'hello';
};
});
})(angular);
// ---SPECS-------------------------
describe('myApp', function () {
beforeEach(module('myApp'));
var scope,controller;
beforeEach(inject(function($rootScope,$controller){
scope = $rootScope.$new(),
controller = $controller('homeController',{$scope:scope});
}));
it('should return true',function(){
expect(scope.helloWorld()).toBe('hello');
});
});
Just create a TestCase here.
And it passed.
Related
I'm adding unit tests to my AngularJS application, and running into an issue where the controller I'm testing is aware of my mocks (logs the correct mock object), but my unit tests cannot return the mock value. I've been stuck on this one for a while.
Mock Service
angular.module('mocks.myService', [])
.factory('myService', function() {
var service = {
hi : 'hi'
};
return service;
});
Controller
.controller('MyCtrl', ['myService', function(myService) {
var vm = this;
vm.doThing = function() {
console.log(myService);
// Object{hi:'hi'}
};
}]);
Unit Test
describe('myApp.selection module', function() {
var myCtrl, myService;
beforeEach(function() {
myService = module('mocks.myService');
console.log(myService);
// undefined
});
describe('controller', function(){
it('should exist', inject(function($controller) {
myCtrl = $controller('MyCtrl');
expect(myCtrl).toBeDefined();
}));
it ('should do thing', function() {
myCtrl.doThing();
});
});
});
Try this:
describe('myApp.selection module', function () {
var myCtrl, myService;
beforeEach(module("mocks.myService"));
beforeEach(inject(function (_myService_) {
myService = _myService_;
console.log(myService);
}));
describe('controller', function () {
it('should exist', inject(function ($controller) {
myCtrl = $controller('MyCtrl');
expect(myCtrl).toBeDefined();
}));
it('should do thing', function () {
myCtrl.doThing();
});
});
});
source: https://docs.angularjs.org/guide/unit-testing
I called one $mdDialog inside a function. I want to unit-test $mdDialog ok and cancel cases.
The below is my controller code (app.controller.js).
(function () {
'use strict';
app.controller('AppCtrl', AppCtrl);
AppCtrl.$inject = ['$scope', '$mdDialog'];
function AppCtrl($scope, $mdDialog) {
$scope.saveEntry = function (ev) {
var confirm = $mdDialog.prompt()
.title('Save Entry')
.textContent('If you want, you can add a description to explain what you changed.')
.placeholder('Version Description')
.ariaLabel('Version Description')
.initialValue('')
.targetEvent(ev)
.ok('Save')
.cancel('Cancel');
$mdDialog.show(confirm).then(function (result) {
$scope.status = true;
}, function () {
$scope.status = false;
});
};
}
})();
The following is the spec code (app.controller.spec.js) :
describe('Unit test AppController: mdDialog', function () {
var $controller, $mdDialog;
beforeEach(function () {
module('App');
inject(function (_$controller_, _$mdDialog_) {
$controller = _$controller_;
$mdDialog = _$mdDialog_;
});
});
it(': Opened', function () {
var $scope = {};
var controller = $controller('AppCtrl', { $scope: $scope });
var $mdDialogOpened = false;
$mdDialog.show = jasmine.createSpy().and.callFake(function () {
$mdDialogOpened = true;
});
$scope.saveEntry();
$scope.$digest();
expect($mdDialog.show).toHaveBeenCalled;
expect($mdDialogOpened).toBe.true;
});
});
when I running the above code I'm getting the following error:
TypeError: Cannot read property 'then' of undefined
I referred this GitHub issue https://github.com/angular/material/issues/1482. But I'm not getting solution for my problem
Thanks in advance
The problem is that you are injecting one version of $mdDialog, and trying to test on another one.
You could try something like this:
describe('Unit test AppController: mdDialog', function () {
var ctrl, mdDialog, scope;
beforeEach(function () {
module('App');
inject(function ($rootScope, $controller, $mdDialog) {
scope = $rootScope.$new();
mdDialog = $mdDialog; //keep the reference, for later testing.
spyOn(mdDialog, 'show');
mdDialog.show.and.callFake(function () {
return {
then: function (callBack) {
callBack(true); //return the value to be assigned.
}
}
});
ctrl = $controller('AppCtrl',{$scope:scope, $mdDialog:mdDialog}); //Inject the dependency
});
});
it(': Opened', function () {
scope.saveEntry(); //exercise the method.
scope.$digest();
expect(mdDialog.show).toHaveBeenCalled();
expect(scope.status).toBe(true);
});
});
Something very similar should work.
hope this help.
I need to write a test case for a method in a controller. In that controller I'm using this instead of $scope. If we use $scope we can able to write the test case as below. But how can i write a test case if i am using this in the controller.
app.controller('ExampleController', function(){
var test = this;
this.testFunction = function(){
return "Hello";
}
});
karma-test case file
describe('app module', function () {
beforeEach(module('testAngularApp'));
describe('ContentController', function () {
var scope, controller;
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller;
controller('ContentController', {
$scope: $scope
});
it('Should return Hello', function () {
expect(scope.testFunction ()).toBe(true);
});
});
Here you go:
//--- CODE --------------------------
(function(angular) {
angular.module('myApp', [])
.controller('ExampleController', function() {
var vm = this;
vm.data = "HI!";
this.testFunction = function(val) {
vm.data = val;
}
});
})(angular);
// ---SPECS-------------------------
describe('myApp', function() {
describe('Example Controller', function() {
var scope, controller;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('ExampleController', {
$scope: scope
});
spyOn(controller, 'testFunction').and.callThrough();
}));
it('expect controller should be defined', function() {
expect(controller).toBeDefined();
});
it('expect scope should be defined', function() {
expect(scope).toBeDefined();
});
it('expect data should be initialized', function() {
expect(controller.data).toEqual("HI!");
});
it('expect data is updated when testFunction is called', function() {
controller.testFunction('Bye!');
scope.$apply();
expect(controller.testFunction).toHaveBeenCalled();
expect(controller.data).toEqual("Bye!");
});
});
});
// --- Runner -------------------------
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
}());
<body>
<!-- because we are testing our controller and not running we don't need a controller or even a module -->
<!-- so there is no ng-app or ng-controller in the markup -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine.css">
<!-- the order that these files load is critical, think twice before changing -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine-html.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/boot.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-mocks.js"></script>
<h2>Finished jasmine unit test</h2>
</body>
So Im trying to figure out how to write unit tests for my angular controller. I am using karma as my runner. I was able to write 1 successful test but every time I try to write another test it yells at me about unexpected calls and such.
Here is my controller im trying to test.
(function (angular) {
'use strict';
var ngModule = angular.module('myApp.dashboardCtrl', []);
ngModule.controller('dashboardCtrl', function ($scope, $http) {
//"Global Variables"
var vm = this;
vm.success = false;
vm.repos = [];
//"Global Functions"
vm.addRepository = addRepository;
vm.listRepos = listRepos;
//Anything that needs to be instantiated on page load goes in the init
function init() {
listRepos();
}
init();
// Add a repository
function addRepository(repoUrl) {
$http.post("/api/repo/" + encodeURIComponent(repoUrl)).then(function (){
vm.success = true;
vm.addedRepo = vm.repoUrl;
vm.repoUrl = '';
listRepos();
});
}
//Lists all repos
function listRepos() {
$http.get('/api/repo').then( function (response){
vm.repos = response.data;
});
}
});
}(window.angular));
So I have a test written for listRepos(). It goes as follows
describe('dashboardCtrl', function() {
var scope, httpBackend, createController;
// Set up the module
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $httpBackend, $controller) {
httpBackend = $httpBackend;
scope = $rootScope.$new();
createController = function() {
return $controller('dashboardCtrl', {
'$scope': scope
});
};
}));
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should call listRepos and return all repos from the database', function() {
var controller = createController();
var expectedResponse = [{id: 12345, url: "https://github.com/myuser/myrepo.git"}];
httpBackend.expect('GET', '/api/repo')
.respond(expectedResponse);
httpBackend.flush();
scope.$apply(function() {
scope.listRepos;
});
expect(controller.repos).toEqual(expectedResponse);
});
This works and the test passes. Now my problem is I want to write another test to test the other function that calls a new api endpoint.
This is the test im trying to write for addRepository.
it('should addRepository to the database', function() {
var controller = createController();
var givenURL = "https://github.com/myuser/myURLtoMyRepo.git";
httpBackend.expect('POST', '/api/repo/' + encodeURIComponent(givenURL)).respond('success');
httpBackend.flush();
scope.$apply(function() {
scope.addRepository(givenURL);
});
expect(controller.success).toBe(true);
expect(controller.listRepos).toHaveBeenCalled();
});
The error I get when I add this test to the spec is:
Error: Unexpected request: GET /api/repo
Expected POST /api/repo/https%3A%2F%2Fgithub.com%2Fmyuser%2FmyURLtoMyRepo.git
at $httpBackend
Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.4.8/$rootScope/inprog?p0=%24digest
The example I am working with is this one here
Any suggestions or tips is greatly appreciated!
UPDATE:
So changed my function to return the promise from the $http.post,
I rewrote my 2nd test and also wrapped my first test in a describe block describing the function its trying to test.
With the following:
describe('addRepository', function () {
it('should addRepository to the database', function () {
var controller = createController();
var givenURL = "https://github.com/myuser/myURLtoMyRepo.git";
httpBackend.expect('POST', '/api/repo/' + encodeURIComponent(givenURL)).respond('success');
scope.$apply(function () {
scope.addRepository(givenURL);
});
httpBackend.flush();
expect(controller.success).toBe(true);
});
it('should call listRepos', function() {
var controller = createController();
httpBackend.expect('GET', '/api/repo').respond('success');
controller.controller().then(function (result) {
expect(controller.listRepos).toHaveBeenCalled();
});
httpBackend.flush();
});
});
I still get the error:
Error: Unexpected request: GET /api/repo
Expected POST /api/repo/https%3A%2F%2Fgithub.com%2Fmyuser%2FmyURLtoMyRepo.git
at $httpBackend
Error: [$rootScope:inprog] $digest already in progress
but also
TypeError: 'undefined' is not a function (evaluating 'controller.controller()')
Error: Unflushed requests: 1
which shows 2 tests failed.
The flush should come after the call to the function. I'd also change the function to return the promise from the $http.post:
// Add a repository
function addRepository(repoUrl) {
return $http.post("/api/repo/" + encodeURIComponent(repoUrl)).then(function (){
vm.success = true;
vm.addedRepo = vm.repoUrl;
vm.repoUrl = '';
listRepos();
});
}
And then in the test you can call it and test the success part:
EDIT
I changed the controller.controller() to what you have.
it('should call listRepos', function() {
// Your setup
ctrl.addRepository().then(function(result) {
expect(ctrl.listRepos).toHaveBeenCalled();
});
});
EDIT 2
I emulated as best i could your code and the tests I write for the code:
(function () {
'use strict';
angular
.module('myApp')
.controller('DashboardController',DashboardController);
DashboardController.$inject = ['$http'];
function DashboardController($http) {
var vm = this;
vm.success = false;
vm.repos = [];
vm.addRepository = addRepository;
vm.listRepos = listRepos;
init();
// Anything that needs to be instantiated on page load goes in the init
function init() {
vm.listRepos();
}
// Add a repository
function addRepository(repoUrl) {
return $http.post('http://jsonplaceholder.typicode.com/posts/1.json').then(function (){
vm.success = true;
vm.addedRepo = vm.repoUrl;
vm.repoUrl = '';
vm.listRepos();
});
}
// Lists all repos
function listRepos() {
return $http.get('http://jsonplaceholder.typicode.com/posts/1').then( function (response){
vm.repos = response.data;
});
}
};
}());
Here I'm using an online JSONPlaceholder API to simulate HTTP calls as I, obviously, can't hit what you're pointing at. And for the test (which all pass):
(function() {
'use strict';
fdescribe('DashBoardController', function() {
var $rootScope, scope, ctrl, $httpBackend;
beforeEach(module('myApp'));
beforeEach(inject(function(_$rootScope_, _$httpBackend_,$controller) {
$rootScope = _$rootScope_;
scope = $rootScope.$new();
$httpBackend =_$httpBackend_;
ctrl = $controller('DashBoardController',{
$scope: scope
});
}));
beforeEach(function() {
// Setup spies
spyOn(ctrl,'listRepos');
});
describe('controller', function() {
it('should be defined', function() {
expect(ctrl).toBeDefined();
});
it('should initialize variables', function() {
expect(ctrl.success).toBe(false);
expect(ctrl.repos.length).toBe(0);
});
});
describe('init', function() {
it('should call listRepos', function() {
$httpBackend.expectGET('http://jsonplaceholder.typicode.com/posts/1')
.respond({success: '202'});
$httpBackend.expectPOST('http://jsonplaceholder.typicode.com/posts/1.json')
.respond({success: '202'});
ctrl.addRepository().then(function(result) {
expect(ctrl.success).toBe(true);
expect(ctrl.repoUrl).toBe('');
expect(ctrl.listRepos).toHaveBeenCalled();
});
$httpBackend.flush();
});
});
});
}());
This is my angular controller :-
angular.module('authoring-controllers', []).
controller('NavCtrl', function($scope, $location, BasketNavigationService) {
$scope.test= function() {
$scope.testVar = BasketNavigationService.showBasketList();
};
});
TEST class
describe('NavCtrl', function() {
var scope, $location, createController;
beforeEach(inject(function ($rootScope, $controller, _$location_) {
$location = _$location_;
scope = $rootScope.$new();
createController = function() {
return $controller('NavCtrl', {
'$scope': scope
});
};
}));
it('should create $scope.testVar when calling test',
function() {
expect(scope.testVar).toBeUndefined();
scope.test();
expect(scope.testVar).toBedefined();
});
});
Getting an error when i run that test case :- scope.test() is undefined..
If i removed BasketNavigationService functionality from controller then it is working..
Please help me to solve that karma test case.
$scope.showBasketList is not a function that can be invoked with $scope.showBasketList(). It is a variable that is equal to the return value of BasketNavigationService.showBasketList().
If you want to reference that function instead it should be $scope.showBasketList = BasketNavigationService.showBasketList; in your controller.
here is the working demo , hope it helps. problem was with injecting the dependencies.
//--- CODE --------------------------
(function(angular) {
// Create module
var myApp = angular.module('myApp', []);
// Controller which counts changes to its "name" member
myApp.controller('MyCtrl', ['$scope', 'BasketNavigationService',
function($scope, BasketNavigationService) {
$scope.test = function() {
$scope.testVar = BasketNavigationService.showBasketList();;
};
}
]);
})(angular);
// ---SPECS-------------------------
describe('myApp', function() {
var scope,
controller;
beforeEach(function() {
module('myApp');
});
describe('MyCtrl', function() {
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope,
'BasketNavigationService': {
showBasketList: function() {
return null;
}
}
});
}));
it('should create $scope.testVar when calling test',
function() {
expect(scope.testVar).toBeUndefined();
scope.test();
// scope.$digest();
expect(scope.testVar).toBeDefined();
});
});
});
// --- Runner -------------------------
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
<script src="http://jasmine.github.io/1.3/lib/jasmine.js"></script>
<script src="http://jasmine.github.io/1.3/lib/jasmine-html.js"></script>
<script src="https://code.angularjs.org/1.2.9/angular.js"></script>
<script src="https://code.angularjs.org/1.2.9/angular-mocks.js"></script>
<link href="http://jasmine.github.io/1.3/lib/jasmine.css" rel="stylesheet" />
fiddle : http://jsfiddle.net/invincibleJai/pf1deoom/1/