Writing Karma test for angularjs app - javascript

I am trying to figure out how to write some simple Karma tests, but am having some trouble with syntax and just understanding the framework. Here is a controller that I want to write a test for:
myApp.controller('menuController', ['$rootScope', '$parse', '$attrs', '$scope', 'Menu', 'Track', '$state',
function($rootScope, $parse, $attrs, $scope, Menu, Track, $state){
$scope.Menu = Menu;
$rootScope.$on("click",function() {
Menu.active = false;
});
$scope.click = function(button) {
Track.event(2, button + "_button_pressed", true);
};
}])
Here is a simple test I wrote for this controller, I am trying to test for the menu button's active status changing when it is clicked:
describe('Controller: menuController', function() {
//load the module with menuController
beforeEach(module('myApp'));
var MainCtrl,
scope;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
MainCtrl = $controller('menuController', {
$scope: scope
});
}));
//put test here
it ('should change $scope.Menu.active to true when button is clicked', function() {
expect(scope.Menu.active).toBeFalse();
scope.click();
expect(scope.Menu.active).toBeTrue();
})
});
After running Grunt test, the test fails and one of the errors it returns is:
TypeError: 'undefined' is not an object (evaluating 'scope.Menu')
I was hoping if anyone can tell me what I'm doing wrong or show me how to write a simple karma test for the controller shown above.
Thanks in advance!

Related

uibModal provider unknown unitTest

I'm starting to learn how to do unitTesting with jasmine. I read a lot of in internet and SO but I can't solve my problem.
I have a directive which has a controller. That controller is using the service $uibModal to open a modal when I click an element. I'm trying to inject that service from my test but I can't. I read a lot of threads saying that I must pass an instance. I'm trying to do so but I can't. Please any help will be appreciated.
.controller('myController', ['$scope', '$uibModal', function($scope, $uibModal){
var self = this;
//OTHER CODE
self.openMyModal = function(dataInput) {
var modalInstance = $uibModal.open({
animation: true,
bindToController: true,
templateUrl: 'app/myComponent/modals/component-modal.html',
controllerAs: 'componentModalCtrl',
controller: 'componentModalController',
windowClass: 'semi-modal semi-modal--large',
scope: $scope
})
}
//OTHER CODE
}
This is the test where I'm trying to mock this modal.
beforeEach(function(){
angular.mock.module('templates');
angular.mock.module('app.components.myComponent');
angular.mock.inject(function($compile, $rootScope, $templateCache, $controller){
scope = $rootScope;
modalInstance = { close: function(){}, dismiss: function(){}, open: function(){}
};
//Initializing element and doing compile and digest
controller = $controller('myController', {$scope: scope, $uibModal: modalInstance});
})
I'm getting the error
Unknown provider: $uibModalProvider <- $uibModal.
Can I inject this service in another way? What I'm doing wrong?
P.S: I have read this Testing AngularUI Bootstrap modal instance controller
Angular ui bootstrap $uibModalInstance breaks down unit tests
Mocking $modal in AngularJS unit tests
Try this one:
beforeEach(module(function ($provide) {
$provide.service("$uibModal", function () {
// mock methods here
});
}));
Finally I solved it. It was a silly error. I imported an additional module that I was missing in my tests. After that I could mock my service and use it without any problem like this.
angular.mock.inject(function($compile, $rootScope, $templateCache, $controller, $uibModal){
scope = $rootScope;
uibModal = $uibModal;
element = angular.element('<directive-tree input-tree=inputTree subsystem=subsystem></directive-tree>');
$compile(element)(scope);
scope.$digest();
controller = $controller('directiveTreeController', {$scope: scope, $uibModal: uibModal});
});

When adding scope to the Angular-Seed default View Controllers the test suite now fails

Using a fresh clone of angular-seed I am attempting some BDD and have added the following tests and code. However, once I add the $scope to the controller, the suite fails on the expect(view1Ctrl).toBeDefined(); expectation.
Below is the only addition I've made and it causes the noted failure when Karma runs.
app/view1/view1.js
.controller('View1Ctrl', ['$scope', function($scope) {
$scope.name = "Name";
}]);
in your test (view1_test.js) you need to inject $scope into the controller...
describe('myApp.view1 module', function() {
beforeEach(module('myApp.view1'));
describe('view1 controller', function(){
it('should ....', inject(function($controller, $rootScope) {
//spec body
var $scope = $rootScope.$new();
var view1Ctrl = $controller('View1Ctrl', {$scope: $scope});
expect(view1Ctrl).toBeDefined();
}));
});
});

AngularJS Karma test - Resolve object passed into controller is undefined when testing

We are using ui-router 0.2.10.
I am injecting a resolve object as a parameter into my controller, which is then setting a scope variable in the controller. It works perfectly on the app like so:
state provider
$stateProvider.state('myState', {
resolve:{
foo: function(){
return 'bar';
},
url: '/',
templateUrl: 'index.html',
controller: 'FooCtrl'
})
controller
app.Controllers.controller('FooCtrl', ['$scope', '$state', 'foo',
function ($scope, $state, $log, Zone, foo) {
$scope.testVar = foo
console.log($scope.testVar);
}])
'Bar' is then logged to the console as expected in Chrome.
But when running tests using Karma, the resolve object is now undefined, which fails the test. Here is the test code:
describe('controllers', function(){
var $rootScope,
$scope,
$state
beforeEach(module('app'))
beforeEach(inject(function($injector) {
$state = $injector.get('$state')
$rootScope = $injector.get('$rootScope')
$scope = $rootScope.$new()
$controller = $injector.get('$controller')
}))
it('FooCtrl should exist', inject( function() {
$state.go('myState')
$rootScope.$apply()
$controller = $controller('FooCtrl', {
'$scope': $scope
})
$rootScope.$apply()
assert.equal($scope.testVar, "bar", "these strings are equal")
}))
})
This error is presented (the resolve object in my case is called resolvedRouteModels):
[$injector:unpr] Unknown provider: fooProvider <- foo
http://errors.angularjs.org/1.3.0-build.2921+sha.02c0ed2/$injector/unpr?p0=fooProvider%20%3C-%20foo
Any help would be much appreciated, and please let me know if you have encountered this problem.
When you instantiate your controller, Angular usually can figure out how to satisfy the controller's dependencies. In this case, it doesn't know about UI-Router's "resolve" functionality.
One way to address this is to supply this dependency yourself in the test, the same way you are passing in the scope to the controller:
var foo = 'bar'; // whatever
$controller = $controller('FooCtrl', {$scope: $scope, foo: foo} );
Note, you could also create a mock $state object and pass that into the controller the same way, if you wanted to incorporate that into your tests.
my assumption is your Angular set up is perfect, if that's the case, you might want to test your code this way. I've used Jasmine 2 syntax.
describe('Foo Controller', function() {
var $scope, $state, controller, Zone, foo, $log;
beforeEach(module('app'));
beforeEach(inject(function($controller) {
$scope = {};
$state = {};
$log = {};
Zone = {};
foo = {};
controller = $controller;
}));
it('should log the value foo', function() {
spyOn(console, 'log');
controller('FooCtrl', { $scope, $state, $log, Zone, foo });
expect($scope.testVar).toEqual({});
expect(console.log).toHaveBeenCalledWith({});
});
it('should log the value foo', function() {
spyOn(console, 'log');
// You could change the value of foo i.e.
foo = 'create more spies than fbi';
controller('FooCtrl', { $scope, $state, $log, Zone, foo });
expect($scope.testVar).toEqual('create more spies than fbi');
expect(console.log).toHaveBeenCalledWith('create more spies than fbi');
});
});
Once again I hope this helps. Peace.

Testing functions not on the scope in the controller

I am using Karma test runner and Jasmine. I know how to test functions on the scope.But in case of a situation like this how do I go about it ?
listing_app.controller('my_listing_products_list', ['$scope', '$modal',
function ($scope, $modal) {
this.someFn = function(a,b){
//How do i test this function ?
}
}]);
How do I get hold of the this object and the controller context through Jasmine ?
Try the following:
describe('my_listing_products_list controller', function(){
beforeEach(inject(function($controller, $rootScope){
scope = $rootScope.$new();
ctrl = $controller("my_listing_products_list", {$scope: scope});
}));
it('should exist', function($controller){
expect(ctrl.someFn()).toBe('whatever the function returns')
});
})

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