Mock Angular JS Controller Jasmine - javascript

I am new to angular js.. just built an sample controller.. and now want to unit test it.. not sure how to write mock it in jasmine..
TestApp.js
var TestApp = angular.module('TestApp');
TestApp.controller('TestCtrl', function($scope) {
$scope.test = "test";
});
})();
TestApp_Spec.js
var scope, ctrl;
//you need to inject dependencies first
beforeEach(inject(function($rootScope) {
$scope = $rootScope.$new();
}));
it('test value should be test', inject(function($controller) {
ctrl = $controller('TestCtrl', {
scope: $scope
});
expect(scope.test).toBe("test");
}));
I am using stand alone version of jasmine and included angular.min.js, angular.mocks.js,TestApp.js and TestApp_Spec.js in the sepc_runner.html
Test results are not showing up..
Need help in writing the correct test cases..

There are some fixes in your code like you've not inserted module in your test suit so jasmine is not able to find the controller.
You have not passed second parameter which is an array as dependency in your module i.e angular.module('TestApp',[]);
Here is the working plunker.
app.js
(function(){
var TestApp = angular.module('TestApp',[]);
TestApp.controller('TestCtrl',["$scope",function(scope) {
alert(scope)
scope.test = "test";
}]);
})();
Test Suit
var scope, ctrl;
describe('MyApp', function() {
beforeEach(module('TestApp'));
//you need to inject dependencies first
beforeEach(inject(function($controller,$rootScope) {
scope = $rootScope.$new();
$controller('TestCtrl', {
'$scope': scope
});
}));
it('test value should be test',function(){
expect(scope.test).toBe("test");
});
});

Related

AngularJS and Jasmine: isolateScope() is undefined

after two days of researching and testing, I need to ask for help.
I am trying to test a directive using Jasmine, and I don't want to include the Karma engine.
In order to see the result of the test, I use the jasmine-html library, and the jasmine css.
I am able to test services easily, but when it comes to testing directive I am not able to access to the isolated scope. One thing to keep in mind is that I don't want to use controllers in my directive, but link functions.
(according to Angular doc, controller should be used only when you want to expose an API to other directives.)
I found multiple answers on StackOverflow, but none of them worked.
The last thing I tried is based on this answered question StackOverflow.
This is the test I am trying to replicate
describe('Wikis Directive Test Suite', function () {
var $scope, scope, elem, directive, linkFn, html;
beforeEach(module('app'));
beforeEach(function () {
html = '<wikis></wikis>';
inject(function ($compile, $rootScope, $templateCache) {
$templateCache.put('templates/wiki-list.html', '<div>wiki template</div>');
$scope = $rootScope.$new();
$scope.wikis = [];
elem = angular.element(html);
elem= $compile(elem)($scope);
//scope = elem.isolateScope(); /*This is always undefined!*/
scope = elem.scope(); /* this doesn't have addWiki, only the empty wikis array */
$rootScope.$digest();
});
});
it('add Wiki should add a valid wiki URL to artist', function () {
var url = 'http://www.foo.com';
scope.newWikiURL = url;
scope.addWiki();
expect(scope.wikis.length).toBe(1);
expect(scope.wikis[0]).toBe(url);
expect(scope.newWikiURL).toBe('');
});
});
The "only" difference, is that I need to use Jasmine 2.4.1 and Angular 1.0.7. Unfortunately it seems like with these libraries the test doesn't work.
The thing is that isolateScope() is always undefined!
I've created a plunker to reproduce the problem.
https://plnkr.co/edit/PRt350VlASShg5oVeY88
Ok, I (actually one of my colleagues found it) what I was doing wrong!
This is the right code which works.
describe('Wikis Directive Test Suite', function () {
var $scope, scope, elem, directive, linkFn, html;
beforeEach(module('app'));
beforeEach(function () {
html = '<wikis></wikis>';
inject(function ($compile, $rootScope, $templateCache) {
$templateCache.put('templates/wiki-list.html', '<div>wiki template</div>');
$scope = $rootScope.$new();
$scope.wikis = [];
elem = angular.element(html);
elem = $compile(elem)($scope);
$scope.$digest();
scope = elem.isolateScope();
});
});
it('add Wiki should add a valid wiki URL to artist',inject( function () {
var url = 'http://www.foo.com';
scope.newWikiURL = url;
scope.addWiki();
expect(scope.wikis.length).toBe(1);
expect(scope.wikis[0]).toBe(url);
expect(scope.newWikiURL).toBe('');
}));
});

JasmineJS: Test angular factory with dependencies

I have a factory defined like this:
angular.module("myServices")
.factory("$service1", ["$rootScope", "$service2", function($rootScope, $service2){...})];
Now, I want to test it, but just injecting $service1 is not working because i get an 'unknown provider' error. So I tried something like that. But I still can't make it work. Why?
beforeEach(function() {
module("myServices");
inject(function ($injector) {
dependencies["$service2"] = $injector.get("$service2");
});
module(function($provide) {
$provide.value("$service1", dependencies["$service2"]);
});
inject(function($injector) {
factory = $injector.get("$service1");
});
});
This is what's working in my tests, using underscores:
describe('Service: $service1', function () {
var $service2, scope;
beforeEach(inject(function (_$service2_, $rootScope) {
$service2 = _$service2_;
scope = $rootScope;
}));
//tests
});
If that still doesn't work, then maybe you're not loading the relevant files (such as service2.js) in your tests.

Jasmine, Angular "rootScope.$broadcast" test

I'm trying to write a test for a controller that has $rootScope.$on('accountsSet', function (event).... So in the tests I'm using .broadcast.andCallThrough() which many other questions here in SO suggest while it also worked before for me.
So my controller is pretty simple:
angular.module('controller.sidemenu', [])
.controller('SidemenuCtrl', function($rootScope, $scope, AccountsService) {
$rootScope.$on('accountsSet', function (event) {
$scope.accounts = AccountsService.getAccounts();
$scope.pro = AccountsService.getPro();
});
});
Any the test is simple as well:
describe("Testing the SidemenuCtrl.", function () {
var scope, createController, accountsService;
beforeEach(function(){
angular.mock.module('trevor');
angular.mock.module('templates');
inject(function ($injector, AccountsService) {
scope = $injector.get('$rootScope');
controller = $injector.get('$controller');
accountsService = AccountsService;
createController = function() {
return controller('SidemenuCtrl', {
'$scope' : $injector.get('$rootScope'),
'AccountsService' : accountsService,
});
};
});
});
it("Should load the SidemenuCtrl.", function () {
accountsService.setPro(true);
spyOn(scope, '$broadcast').andCallThrough();
var controller = createController();
scope.$broadcast("accountsSet", true);
expect(scope.pro).toBeTruthy();
});
});
The error I'm getting if for spyOn(scope, '$broadcast').andCallThrough();. Note that scope for this tests is rootScope so that shouldn't be a problem.
So the error that refers to that line:
TypeError: 'undefined' is not a function (evaluating 'spyOn(scope, '$broadcast').andCallThrough()')
at .../tests/controllers/sidemenu.js:30
I'm turning my comment into an answer since it turned out to be the solution:
In jasmine 2.0 the syntax of spies has changed (and many other things, see the beautiful docs here)
the new syntax is
spyOn(foo, 'getBar').and.callThrough();
Compare with the jasmine 1.3 syntax of:
spyOn(foo, 'getBar').andCallThrough();

Trying to test Angular with Karma/Jasmine, but can't get the Jasmine tests to set $scope correctly

My Angular script looks like this:
var app = angular.module("myApp", []);
app.controller('TestController', function ($scope) {
$scope.test= "TEST";
});
My test file looks like this:
describe('first test', function() {
var $scope;
beforeEach(function (){
module('myApp');
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('TestController', {
$scope: $scope
});
});
it('scope should have test', function() {
expect($scope.test).toEqual("TEST");
});
});
This test fails saying $scope.test is undefined. I debugged in Chrome and saw that $scope has a bunch of properties on it, but none of them are test. I've looked through several examples online, and they all look pretty similar to this. i'm not quite sure what i'm doing wrong here and i'm stuck....
edit
I tried adding $controller to inject, but i'm still having the same problem.
You need to pass $controller service alongside with $rootScope:
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('TestController', {
$scope: $scope
});
});

using the mock folder for karma test in yeoman angularjs

I have a angularjs application, which I generated with yeoman. In the karma.conf.js is a reference to test/mock/**/*.js. I have troubles to find out, how I use this folder. Currently I have a simple Service:
'use strict';
angular.module('tvcalApp')
.factory('Series', function ($resource) {
return $resource('/search/:search');
});
and a Test
'use strict';
var $httpBackend;
describe('Service: Series', function () {
// load the service's module
beforeEach(module('tvcalApp'));
// instantiate service
var Series;
beforeEach(inject(function (_Series_) {
Series = _Series_;
}));
beforeEach(inject(function ($injector) {
var url_get = '/search/The%20Simpsons';
var response_get = [{"seriesid": "71663"}];
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET(url_get).respond(response_get);
}));
it('should return a list if search for The Simpsons', function () {
var res = Series.query({search: 'The Simpsons'});
$httpBackend.flush();
expect(res[0].seriesid === 71663);
});
});
This is working. But I wonder If I could use the mock folder from the karma.conf.js for the mocking function. Is it possible to move the mock part into the mock folder and use it for all unit test?
I could not find any example or documentation for this folder. Can someone please point me to to an example or documentation how to use the mock folder.
Basically i have done something like this looking at angular-mocks.js:
Let's say may app is called ql. and i have a loginService that i want to mock:
mocks/servicesMock.js looks like this:
'use strict';
var ql = {};
ql.mock = {};
ql.mock.$loginServiceMockProvider = function() {
this.$get = function() {
var $service = {
login: function() { }
};
return $service;
};
};
angular.module('qlMock', ['ng']).provider({
$loginServiceMock: ql.mock.$loginServiceMockProvider
});
Then in my tests i can injeck $loginServiceMock:
'use strict';
describe('LoginController tests', function () {
// load the controller's module
beforeEach(module('ql'));
// load our mocks module
beforeEach(angular.mock.module('qlMock'));
var loginController,
loginServiceMock,
scope;
// Initialize the controller and a mock scope
// $loginSericeMock will be injected from serviceMocks.js file
beforeEach(inject(function ($controller, $rootScope, $loginServiceMock) {
scope = $rootScope.$new();
loginServiceMock = $loginServiceMock;
loginController = $controller('LoginController', {
$scope: scope,
loginService: loginServiceMock
});
}));
});
The example by #gerasalus is useful, but to answer the question:
mocks is just a folder to put your code in to keep your project organized and the code in tests short and to the point. By keeping all your mocks in one place, it is easier to reuse them in tests... copying them from one test to another would be bad practice from a DRY perspective.
So, for example, you might have a service called 'Foo'
app/service/foo.js
Then you might create a mock of that service, called 'FooMock'
test/mocks/service/foo.js
And then you would create tests and inject whatever mocks you need, as is shown in gerasulus's answer.

Categories