Cannot make controller tests working with AngularJS + RequireJS - angularjs

Can’t figure out how to make controller tests working. I am playing with ngStart seed project. So I forked the repository (https://github.com/Havrl/ngStart ) and want to create a very basic unit test.
My controller test file:
define(function() {
"use strict";
describe("the contactcontroller", function () {
var contactController, scope;
beforeEach(function () {
module("contact");
inject(["ContactController", function (_contactController) {
contactController = _contactController;
}]);
});
it("should give me true", function () {
expect(true).toBe(true);
});
});
});
But that is not working.
What am I missing?

as answered in the following question the controller needs to be manually instantiated with a new scope:
how to test controllers created with angular.module().controller() in Angular.js using Mocha
Additionally the project you are using (its mine :-) ) is defining the controllers inside route definitions and not with a call to angular.controller(...).
The downside is that the controllers are not known by name to angularJS (afaik), so the code from the answer above would not work:
ctrl = $controller("ContactController", {$scope: scope });
Instead you have to load the controller explicitely with requireJS inside your test file and give the function to the $controller(..) call, like this:
define(["ContactController"], function(ContactController) {
"use strict";
describe("the contactcontroller", function () {
var contactController, scope;
beforeEach(function () {
module("contact");
inject(["$rootScope", "$controller", function ($rootScope, $controller) {
scope = $rootScope.$new();
contactController = $controller(ContactController, {$scope: scope});
}]);
});
....
});

Related

jasmine mocking service inside service

I'm testing a directive ('planListing') that has a dependency on a service called 'planListingService'. This service has a dependency to another service called 'ajax' (don't shoot the messenger for the bad names).
I'm able to compile the directive, load its scope and get the controller WITH A CAVEAT. As of now I am being forced to mock both services 'planListingService' and 'ajax' otherwise I will get an error like this:
Error: [$injector:unpr] Unknown provider: ajaxProvider <- ajax <- planListingService
http://errors.angularjs.org/1.3.20/$injector/unpr?p0=ajaxProvider%20%3C-%20ajax%20%3C-%20planListingService
I thought that because I was mocking up the 'planListingService' that I wouldn't have to actually bother with any implementation nor any dependencies of this service. Am I expecting too much?
Here is the code in a nutshell:
planListing.js
angular.module('myApp')
.directive('planListing', planListing)
.controller('planListingCtrl', PlanListingCtrl);
function planListing() {
var varDirective = {
restrict: 'E',
controller: PlanListingCtrl,
controllerAs: 'vm',
templateUrl: "scripts/directives/planListing/planListing.html";
}
};
return varDirective;
}
PlanListingCtrl.$inject = ['planListingService'];
function PlanListingCtrl(planListingService) {
...
}
planListingService.js
angular.module('myApp')
.factory('planListingService', planListingService);
planListingService.$inject = ['$q', 'ajax'];
function planListingService($q, ajax) {
...
}
ajax.js
angular.module('myApp')
.factory('ajax', ['backend', '$browser', 'settings', '$http', '$log',
function (backend, $browser, settings, $http, $log) {
...
planListing.spec.js
describe('testing planListing.js',function(){
var el,ctrl,scope,vm;
var service;
module('myApp');
module('my.templates');
beforeEach(module(function ($provide){
// This seems to have no effect at all, why?
$provide.service('planListingService', function () {
this.getAllPricePlans=function(){};
});
// I don't get the error if I uncomment this:
// $provide.service('ajax', function ($q) {
// this.getAllPricePlans=function(){};
// });
}));
beforeEach(function() {
module('myApp');
module('my.templates');
});
beforeEach(angular.mock.inject(function (_$compile_,_$rootScope_,_$controller_){
$compile=_$compile_;
$rootScope = _$rootScope_;
$controller = _$controller_;
el = angular.element('<plan-listing></plan-listing>');
scope = $rootScope.$new();
$compile(el)(scope);
scope.$digest();
ctrl = el.controller('planListing');
scope = el.isolateScope() || el.scope();
vm = scope.vm;
}));
describe('testing compilation / linking', function (){
it('should have found directive and compiled template', function () {
expect(el).toBeDefined();
expect(el.html()).not.toEqual('');
expect(el.html()).toContain("plan-listing-section");
});
});
it('should have a defined controller',function(){
expect(ctrl).toBeDefined();
});
it('should have a defined scope',function(){
expect(ctrl).toBeDefined();
});
});
So why is that I need to mock up the 'ajax' service even though I am mocking up 'planListingService' which is the one calling the 'ajax' service?
Thanks!
I have been there... feels like bad start But i think your directive is depend on the service and you need to inject it in order to directive can work with this, Just by calling directive it doesn't mean that it's going to inject it in your test. It will look for it and if it's not injected it will give you error
you could do so before testing your directive
beforeEach(inject(function ($injector) {
yourService = $injector.get('yourService');
})
For documentation purposes, here is the answer (thanks #estus for noticing this):
Indeed the problem was related to the incorrect initialization of my modules. Instead of this:
describe('testing planListing.js',function(){
var el,ctrl,scope,vm;
var service;
module('myApp');
module('my.templates');
...
I should've done this:
describe('testing planListing.js',function(){
var el,ctrl,scope,vm;
var service;
beforeEach(module('myApp'));
beforeEach(module('my.templates'));
...
After that things started working again as expected.

Ionic Framework - Unit testing controller using $ionicView.enter

I'm trying to unit test a controller that uses $ionicView.enter like this
myControllers.controller('MyCtrl', [
'$scope',
function($scope) {
'use strict';
$scope.$on('$ionicView.enter', function(){
$scope.myValue = 1;
});
}]);
In my unit test I can spy on the event and check it's been called, but it doesn't then get into the function and set $scope.myValue. This is my test:
it('should do stuff', function () {
spyOn(scope, '$on').and.callThrough();
scope.$on('$ionicView.enter');
// this passes
expect(scope.$on).toHaveBeenCalledWith('$ionicView.enter')
// this fails - scope.myValue is undefined
expect(scope.myValue).toEqual(1);
});
I thought callThrough would call the code inside the function I'm spying on, but doesn't seem to here.
Any help appreciated.
The answer was to trigger the ionicView enter event after my controller set up in the test rather than try to spy on it.
beforeEach(inject(function($controller) {
scope = $rootScope.$new();
$controller('MyCtrl', {
$scope: scope
});
scope.$emit('$ionicView.enter');
scope.$digest();
}));

'myApp.controller is not a function' error in Jasmine

I'm getting the following error in the js console log when attempting to assign the controller to 'myCtrl' variable in a jasmine test: 'myApp.controller is not a function'
In the controller itself, the controller is defined as the following and this is what's triggering the error I mentioned:
myApp.controller('myCtrl', ...
And this is how I'm trying to access the controller in my spec file:
beforeEach(function() {
module('myApp');
});
it('tests the controller', inject(function($controller) {
var myCtrl = $controller('myCtrl');
}));
Any idea why it's still throwing this error? Seems to be a missing dependency but not sure where..
If you want to test your controller , following is one way of writing a test case
describe('YourControllerName', function () {
var $rootScope, scope, $controller ;
beforeEach(angular.mock.module('YourModuleName'));
beforeEach(inject(function ($rootScope, $controller ) {
scope = $rootScope.$new();
$controller('YourControllerName', {
$scope: scope
});
}));
it('Should do this', function () {
//assertions
});
});
Thanks - It turns out it was something as simple as I need to list myApp.js before myController.js in the SecRunner.html. I was pretty sure I tried that previously.. but there you go.

Jasmine test run with Karma cannot find Angular method called from onload

First off I am new to Karma, I have the following code in an application...
$(document).ready(function() {
angular.element('#eventListController').scope().initEvents();
});
but when I run karma I see....
Uncaught TypeError: Cannot read property 'initEvents' of undefined
What am I missing here? I am using a Sanity check test...
describe("Sanity test for jasmine", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
Update
So I think I am having this issue because there is no DOM right now. How do I mock out this DOM I guess would be the real question.
new version still failing...
describe("Sanity test for jasmine", function() {
beforeEach(module("Events"));
describe("TailsCtrl", function () {
var scope,
controller;
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller;
}));
it("should assign message to hello world", function () {
expect(true).toBe(true);
});
});
});
Update
Looks like I need jasmine-jquery but it fails to install on my local windows GRRRRRR
What are you testing? A controller? A directive? You have to provide a real scope for your tests. Here is an example for a controller test in jasmine:
describe("controller", function() {
beforeEach(function() {
$scope = $rootScope.$new();
HomeCtrl = $controller('HomeCtrl', { $scope: $scope, $rootScope: $rootScope });
$rootScope.$digest();
});
it("foobar", function() {});
});
Now your controller has a real scope, mocked out for your tests.
Looks like I need a couple plugins and something like this will work
http://a-developer-life.blogspot.com/2011/06/jasmine-part-2-spies-and-mocks.html

Unit testing socket.io with karma and jasmine

The project I am working on makes use of socket.io for some of the components of the UI.
I am trying to write unit tests for this particular section of the application. I am using: angular-socket-io and angular-socket.io-mock to mock the server side component.
I am using everything at the simplest level, so I have my factory:
myapp
.factory('notify', function (socketFactory) {
return socketFactory();
});
This is the controller
myapp
.controller('NotificationsCtrl', function ($scope, notify) {
$scope.items = []
notify.emit("loadItems",{},function(res){
$scope.items = res.res
});
});
and finally the unit test:
describe('Controller: NotificationsCtrl', function () {
// load the controller's module
beforeEach(module('app'));
var NotificationsCtrl,
scope;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
NotificationsCtrl = $controller('NotificationsCtrl', {
$scope: scope
});
}));
it('The scope.items should change somehow', function() {
expect(scope.items.length).toEqual(3);
});
});
I cannot realize what is missing to make this working.
How should I change my code to make it happen?
Thanks

Resources