I have created a scope method inside my controller which is executing when a button is pressed. I am writing unit test cases for the same. I have injected my module in beforeEach block and created spyon my scope function and then using it in 'it' method and checking whether it is called or not. But getting an error as a method not found.
Controller
'use strict';
angular.module('myApp.view1', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'
});
}])
.controller('View1Ctrl', ['$scope',View1Ctrl])
function View1Ctrl($scope) {
$scope.user = {
name: '',
last: ''
}
$scope.showFormData = function() {
$scope.formData = $scope.user.name + $scope.user.last;
}
}
spec.js
'use strict';
describe('myApp.view1 module', function () {
var $controller, $rootScope;
beforeEach(module('myApp.view1'));
beforeEach(inject(function (_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
describe('view1 controller', function () {
var $scope, controller, formData;
beforeEach(function () {
$scope = $rootScope.$new();
controller = $controller('View1Ctrl', {
$scope: $scope
});
spyOn(controller, 'showFormData');
});
it('should check for the show form details', function () {
$scope.user.name = "Sandeep";
$scope.user.last = "Gupta";
expect($scope.showFormData).toHaveBeenCalled();
expect($scope.user.name + $scope.user.last).toEqual(firstname);
});
});
});
Need help to resolve this issue.
It looks like you're trying to spy on the showFormData method of the controller:
spyOn(controller, 'showFormData');
However, the showFormData doesn't exist on the controller, it's a method of the controller's scope.
You'll need to use:
spyOn($scope, 'showFormData');
It's also important to know that you need to use the same object to both spyOn and expect(...).toHaveBeenCalled(). In your case you where spying on controller.showFormData(), yet expecting $scope.showFormData() to have been called.
Related
It's a very simple test.. and it's not passing.. If someone can throw some light into this :)
This is the controller code (part of it) that needs to be tested
AppCtrl
$scope.requestAuthorization = function() { requestAuthorization(); };
if ($stateParams.requestAuthorization === true) {
console.log('$stateParams.requestAuthorization');
$scope.requestAuthorization();
}
function requestAuthorization() {
console.log('requestAuthorization()');
// more code here..
}
Test
describe('AppCtrl', function() {
var AppCtrl, $rootScope, $scope, $stateParams;
beforeEach(module('myapp'));
// disable ionic cache to avoid GET errors
beforeEach(module(function($provide, $urlRouterProvider) {
$provide.value('$ionicTemplateCache', function() {});
$urlRouterProvider.deferIntercept();
}));
beforeEach(inject(function($controller, _$rootScope_, _$injector_, _$stateParams_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$stateParams = _$stateParams_;
AppCtrl = $controller('AppCtrl',{
$scope: $scope
});
spyOn($scope, 'requestAuthorization');
$stateParams.requestAuthorization = true;
}));
it('$stateParams.requestAuthorization should be defined', function() {
expect($stateParams.requestAuthorization).toBeDefined();
});
it('$scope.requestAuthorization should be defined', function() {
expect($scope.requestAuthorization).toBeDefined();
});
// this test is not passing..
it('should call requestAuthorization', function() {
expect($scope.requestAuthorization).toHaveBeenCalled();
});
});
The function is actually being called, I can see the console.log in the console, but it's not passing.
Easy tests, all passing.. except the last one..
Thanks for your time :)
NOTE: There is a $stateParams.requestAuthorization, and a $scope.requestAuthorization. First one is boolean, the other a function, the function is not passing.
In your beforeEach block, define the $stateParams before instanciate the Controller.
beforeEach(inject(function($controller, _$rootScope_, _$injector_, _$stateParams_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$stateParams = _$stateParams_;
$stateParams.requestAuthorization = true;
AppCtrl = $controller('AppCtrl',{
$scope: $scope,
$stateParams: $stateParams
});
spyOn($scope, 'requestAuthorization');
}));
Where I make mistake? How can I get instance of controller in Jasmine + angular? How can I resolve controller? I have no idea what should I use to resolve that.
'use strict';
angular.module('myApp.contact', ['ui.router'])
.controller('contactCtrl', ['$scope', function ($scope) {
$scope.contact = {
name: 'John Doe'
};
}]);
describe('myApp.contact module tests', function () {
var scope, createController;
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
createController = function () {
return $controller('contactCtrl', {
'$scope': scope
});
};
}));
it('contact name is John Doe', function () {
var controller = createController();
expect(controller).toEqual('John Doe');
});
});
myApp.contact module tests
✗ contact name is John Doe
Error: [ng:areq] Argument 'contactCtrl' is not a function, got undefined
http://errors.angularjs.org/1.4.9/ng/areq?p0=contactCtrl&p1=not%20a%20function%2C%20got%20undefined
at E:/angular-seed/app/bower_components/angular/angular.js:68:12
at assertArg (E:/angular-seed/app/bower_components/angular/angular.js:1816:11)
at assertArgFn (E:/angular-seed/app/bower_components/angular/angular.js:1826:3)
You had missed several things here
You need to initialize angular module to make your controller & all other components available by doing module('myApp.contact') in your before each so that Error: [ng:areq] Argument 'contactCtrl' is not a function, got undefined will went away.
Then inside your Assert statement of test you should use scope object instead of controller(you could use controller when your properties are bounded to this)
Don't forget to refer contactCtrl.js & ui-router.js on page.
expect(scope.contact.name).toEqual('John Doe');
Code
describe('myApp.contact module tests', function () {
var scope, createController;
beforeEach(module('myApp.contact')); //1st change
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
createController = function () {
return $controller('contactCtrl', {
'$scope': scope
});
};
}));
it('contact name is John Doe', function () {
var controller = createController();
expect(scope.contact.name).toEqual('John Doe'); //2nd change
});
});
In my Angular app, UI router resolves a promise into the controller. When trying to test this controller, Karma is complaining about an unknown provider. How do I inject a fake object into the test to represent this resolve object.
My app's code looks something like:
angular.module('myapp')
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tab.name', {
...
resolve: {
allTemplates: function(Templates) {
return Templates.all().then(function(templates) {
return templates;
});
}
}
})
})
.controller('QueriesCtrl', function(allTemplates, UserQuery) {
var vm = this;
vm.queries = allTemplates;
vm.goToUrl = function(index, data) {
var processedUrl = UserQuery.process(data, vm.queryTyped[index]);
UserQuery.goToUrl(processedUrl);
};
});
When trying to run tests I get the error
Unknown provider: allTemplatesProvider <- allTemplates <- QueriesCtrl
I've tried creating a spy and injecting it, but this does not work. Here's my test at the moment:
describe('Unit: queriesCtrl', function() {
var controller,
scope,
UserQuery;
beforeEach(function() {
module('myapp');
inject(function($injector) {
UserQuery = $injector.get('UserQuery');
allTemplates = jasmine.createSpyObj('allTemplates', [{a:1}, {a:2}, {b:3}]);
});
});
describe('goToUrl', function() {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('QueriesCtrl as ctrl', {
'$scope': scope
});
}));
it('should call UserQuery.process()', function() {
spyOn(UserQuery, 'process');
scope.ctrl.goToUrl();
expect(UserQuery.process).toHaveBeenCalled();
});
});
});
Since there is no route involved in unit test you would have to inject the allTemplates as a normal object with $controller function. Can you try:
controller = $controller('QueriesCtrl as ctrl', {
'$scope': scope,
'allTemplates':allTemplates
});
Else you can use the $provide API to create a dummy service.
module(function ($provide) {
$provide.value("allTemplates", {[{a:1}, {a:2}, {b:3}]});
Do it first thing in your beforEach block.
I am trying to write a unit test to test that tests a factory that performs a http.get and then tests the scope bindings.
The factory is called within my controller.
Here's a plunker showing my http.get: http://plnkr.co/edit/VqUSeTiEj3MP37tAXKad?p=preview
Ctrl:
app.controller('MainCtrl', function($scope, $http, factoryGetJSONFile) {
$scope.name = 'World';
factoryGetJSONFile.getMyData(function(data) {
$scope.Addresses = data.Addresses.AddressList;
$scope.People = data.Names.People;
$scope.Country = data.Country;
});
});
Test:
describe('with httpBackend', function () {
var app;
beforeEach(function () {
app = angular.mock.module('plunker')
});
describe('MyCtrl', function () {
var scope, ctrl, theService, httpMock;
beforeEach(inject(function ($controller, $rootScope, factoryGetJSONFile, $httpBackend) {
httpMock = $httpBackend;
scope = $rootScope.$new();
ctrl = $controller('MyCtrl', {
$scope: scope,
factoryGetJSONFile: factoryGetJSONFile,
$httpBackend: httpMock
});
}));
it("should make a GET call to data.json", function () {
console.log("********** SERVICE ***********");
httpMock.expectGET("data.json?").respond(data);
console.log(data.Addresses);
console.log(data.Names);
console.log(data.Country);
//expect(factoryGetJSONFile.getMyData()).toBeDefined();
httpMock.flush();
});
})
});
The test for the http.get seems ok, but when i try logging the reponse (data), an error occurs.
UPDATE:
When i try to log the call via:
console.log(httpMock.expectGET("data.json?").respond(data));
Undefined is displayed.
I have an AngularJS controller test script using PhantomJS. The test looks to see if the controller has loaded "users" data from a database via a RESTFul web service using AngularJS' $resource service. The problem is that the test fails because the $resource (which returns a promise I believe) isn't resolved yet when the test executes. What's the proper way to deal with this delay so that the test will pass? Here is my code:
CONTROLLER:
.controller('MainCtrl', function ($scope, Users) {
$scope.users = Users.query();
$scope.sortField = 'lastName';
$scope.reverseSort = true;
})
SERVICE:
angular.module('clearsoftDemoApp').factory('Users', function ($resource) {
return $resource('http://localhost:8080/ClearsoftDemoBackend/webresources/clearsoft.demo.users', {}, {
query: {method: 'GET', isArray: true}
});
});
TEST:
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('clearsoftDemoApp'));
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 retrieve a list of users and assign to scope.users', function () {
expect(scope.users.length).toBeGreaterThan(0);
});
});
You need to mock the factory call and pass the mock to the controller:
beforeEach(inject(function ($controller, $rootScope) {
var users = { query: function() { return [{}]; } };
scope = $rootScope.$new();
MainCtrl = $controller('MainCtrl', {
$scope: scope,
Users: users
});
}))