I'm trying to test a really simple directive with AngularJS 1.5.5.
The directive itself :
angular.module('whatToPlayWithFriendsApp.card').directive('card', function () {
return {
restrict: 'EA',
link: function link(scope, element, attrs) {
element.bind('click', function () {
angular.element(this).toggleClass('selected');
});
}
};
});
The test:
'use strict';
describe('Directive: card', function () {
// load the directive's module
beforeEach(module('myApp.card'));
var element, scope;
beforeEach(inject(function ($rootScope) {
scope = $rootScope.$new();
}));
it('should make element to have selected class on click', inject(function ($compile) {
element = angular.element('<div card></div>');
$compile(element)(scope);
scope.$digest();
element.triggerHandler('click');
expect(element.hasClass('selected')).toBe(true);
}));
});
But my test fails because of this error :
Undefined is not a constructor (evaluating 'expect(element.hasClass('selected')).toBe(true)')
I looked up at this issue : https://github.com/angular/angular.js/issues/14251
, but I'm using the same version for all angularjs suite. What am I missing here?
Using gulp for the task I run them with : (gulp test:client ):
gulp.task('test:client', ['wiredep:test', 'constant'], (done) => {
new KarmaServer({
configFile: `${__dirname}/${paths.karma}`,
singleRun: true
}, done).start();
});
gulp.task('wiredep:test', () => {
return gulp.src(paths.karma)
.pipe(wiredep({
exclude: [
'/json3/',
'/es5-shim/',
/font-awesome\.css/
],
devDependencies: true
}))
.pipe(gulp.dest('./'));
});
gulp.task('constant', function() {
let sharedConfig = require(`./${serverPath}/config/environment/shared`);
return plugins.ngConstant({
name: 'myApp.constants',
deps: [],
wrap: true,
stream: true,
constants: { appConfig: sharedConfig }
})
.pipe(plugins.rename({
basename: 'app.constant'
}))
.pipe(gulp.dest(`${clientPath}/app/`))
});
Karma files :
// list of files / patterns to load in the browser
files: [
// bower:js
'client/bower_components/jquery/dist/jquery.js',
'client/bower_components/angular/angular.js',
'client/bower_components/angular-resource/angular-resource.js',
'client/bower_components/angular-cookies/angular-cookies.js',
'client/bower_components/angular-sanitize/angular-sanitize.js',
'client/bower_components/lodash/dist/lodash.compat.js',
'client/bower_components/angular-ui-router/release/angular-ui-router.js',
'client/bower_components/semantic/dist/semantic.js',
'client/bower_components/moment/moment.js',
'client/bower_components/angular-moment/angular-moment.js',
'client/bower_components/angular-mocks/angular-mocks.js',
// endbower
'client/app/app.js',
'client/{app,components}/**/*.module.js',
'client/{app,components}/**/*.js',
'client/{app,components}/**/*.{jade,html}'
],
Phantom JS "phantomjs-prebuilt": "^2.1.4"
I'm so used of Jasmine, I didn't realize I was on Chai assertion library.
Related
This is my js file code
$scope.close = function () {
modalInstance.close();
};
unit test case for the js code
beforeEach(inject(function($rootScope) {
var modalInstance = { close: function() {}, dismiss: function() {} };
$controller('BusinessRuleEditCtrl',
{
$scope: scope,
dataFactory: mockdataFactory,
$modal: modal,
modalInstance:modalInstance
});
}));
it(' scope.close () functionality is checked ', function () {
scope.close();
spyOn(modalInstance, 'close').toHaveBeenCalled();
});
when i try to run this spec iam getting error "Cannot read property 'close' of undefined".any suggesion will be appreciated....thanks in advance.
I have a service that depends on another service from a different module like so:
(function() {
'use strict';
angular
.module('app.core')
.factory('userService', userService);
function authService() {
return: {
userLoggedIn: false
}
}
})();
(function() {
'use strict';
angular
.module('app.services')
.factory('AuthService', authService);
authService.$inject = ['$http', 'userService'];
function authService($http, userService) {
}
I'm trying write tests for my authService but am getting injection errors since it can't find userService
beforeEach(function() {
module('app.services');
});
beforeEach(inject(function(_AuthService_) {
authService = _AuthService_;
}));
How can I overcome this, will using $provide help me here?
UPDATE
I have attempted the following, but still getting the error
beforeEach(function() {
module('app.services');
});
beforeEach(inject(function(_AuthService_, _$provide_) {
authService = _AuthService_;
$provide = _$provide_;
}));
beforeEach(function() {
module(function ($provide) {
$provide.value('userService', function(){
return {
userLoggedIn: false
}
});
});
});
SOLVED
Ok, so I just needed to do the following:
beforeEach(function() {
module('app.dataservices');
module(function ($provide) {
$provide.value('userService', function(){
return {
userLoggedIn: false
}
});
});
});
beforeEach(inject(function(_AuthService_) {
authService = _AuthService_;
}));
Tests are now passing fine for me
Let's say you service uses the $state service and you want to mock id. Specifically the get method. Then you just need to add inside the first describe something like this.
beforeEach(function () {
module(function ($provide) {
$provide.service('$state', function() {
return {
get: function() {}
}
});
});
});
In this gist you can find some interesting examples of mocking services using $provide.
you should be preloading all services in your karma.conf.js (i assume you are using karma).
here is our karma.conf.js file ...
/**
* Karma test runner configuration
*/
'use strict';
module.exports = function (config) {
config.set({
basePath: './',
browsers: ['PhantomJS'],
frameworks: ['jasmine'],
reporters: ['mocha', 'coverage'],
singleRun: true,
preprocessors: {
'src/**/!(*spec)*.js': ['coverage'],
'dest/**/*.html': ['ng-html2js']
},
ngHtml2JsPreprocessor: {
stripPrefix: 'dest/',
moduleName: 'ngHtmlFiles'
},
coverageReporter: {
type: 'html',
dir: 'coverage'
},
files: [
'dest/vendor.min.js',
'bower_components/angular-mocks/angular-mocks.js',
'src/**/*.js',
'dest/**/*.html'
]
});
};
I am trying to set up Jasmine testing on my Angular Application to test a controller.
Controller:
var navigation = angular.module("navigation", []);
navigation.controller("NavigationController", ['$scope', function ($scope) {
$scope.myObject = [];
$scope.tabs = [
{ title: "Surcharge Index", content: "SurchargeIndex" },
{ title: "Scheduling and Adjustments", content: "Scheduling" },
{ title: "Auto Update Settings", content: "Automation" },
{ title: "Processing Rules", content: "FuelProcessing" },
{ title: "Data Update ", content: "DataUpdate" },
];
}]);
Test:
describe("NavigationController", function () {
var scope;
var controller;
//beforeEach(module('app'));
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
controller = $controller('NavigationController', { '$scope': scope });
}));
it("scope is defined", function () {
expect(scope).toBeDefined();
//expect(scope.tags[0].title).toBe('Doe Index');
});
it("should contain a list of tabs", function () {
//expect(scope).toBeDefined();
expect(scope.tags).toContain({ title: 'Doe Index' });
});
});
Neither Jasmine test is ever run.
Test page:
Jasmine2.0.0finished in 0.001s
raise exceptions
Ran 0 of 2 specs - run all
0 specs, 0 failures
NavigationController
scope is defined
should contain a list of tabs
This is what Jasmine returns. For some reason none of the tests are being run.
Any suggestions?
You have to load the module you are testing:
beforeEach(module('navigation'));
Add that where you have:
//beforeEach(module('app'));
But uncommented.
I used this OdeToCode post as a sample once I realized that I needed to load the module. My test case now looks like this.
describe("myApp", function () {
beforeEach(module('navigation'));
describe("NavigationController", function() {
var scope;
var controller;
beforeEach(inject(function($controller, $rootScope) {
jasmine.addMatchers(customMatcher);
scope = $rootScope.$new();
controller = $controller('NavigationController', { '$scope': scope });
}));
it("scope is defined", function() {
expect(scope).toBeDefined();
});
it("should contain a list of tabs", function() {
expect(scope.tabs.length).toBe(5);
});
});
)};
I did read the angular official tutorial and launch test with success. But I tried to make test for a simple controller (when you click on button a boolean change), i have the following error : ReferenceError: Can't find variable: $scope
controller :
siteLogic = angular.module('siteLogic', []);
siteLogic.controller('siteCtrl', ['$scope',function($scope, initMap) {
$scope.addingSite=false;
$scope.changeAddingSite = function(){
$scope.addingSite= !$scope.addingSite;
};
}]);
file for test :
describe('$scope initialisation', function() {
beforeEach(module('siteLogic'));
describe('sets the addingSite at the inverse', function() {
var scope, controller;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
//it's on the next line I have the error indication
controller = $controller('siteCtrl', { $scope: scope });
}));
it('sets true if false', function() {
scope.addingSite = false;
scope.changeAddingSite();
expect(scope.addingSite).toEqual(true);
});
it('sets false if true', function() {
$scope.addingSite = true;
$scope.changeAddingSite();
expect($scope.addingSite).toEqual(false);
});
});
});
karma.config.js
module.exports = function(config) {
config.set({
basePath: '..',
frameworks: ['jasmine'],
files: [
'test/dep/angular-1.3.15/angular.js',
'test/dep/angular-1.3.15/angular-resource.js',
'test/dep/angular-1.3.15/angular-route.js',
'test/dep/angular-1.3.15/angular-mocks.js',
'lib/map/public/angular/*.js',
'test/map/*.js'
],
autoWatch: true,
browsers: ['PhantomJS']
});
};
In the second group of test change $scope to scope
it('sets false if true', function() {
$scope.addingSite = true; // there is no $scope
$scope.changeAddingSite();
expect($scope.addingSite).toEqual(false);
});
var siteLogic = angular.module('siteLogic', []);
siteLogic.controller('siteCtrl', ['$scope',function($scope) {
$scope.addingSite=false;
$scope.changeAddingSite = function(){
$scope.addingSite= !$scope.addingSite;
};
}]);
describe('$scope initialisation', function() {
beforeEach(module('siteLogic'));
describe('sets the addingSite at the inverse', function() {
var scope, controller;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
//it's on the next line I have the error indication
controller = $controller('siteCtrl', { $scope: scope });
}));
it('sets true if false', function() {
scope.addingSite = false;
scope.changeAddingSite();
expect(scope.addingSite).toEqual(true);
});
it('sets false if true', function() {
scope.addingSite = true;
scope.changeAddingSite();
expect(scope.addingSite).toEqual(false);
});
});
});
<link href="http://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet"/>
<script src="http://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>
I am trying to create a basic test to verify that I can create a controller or service.
My app is is the following directory
app/js/app.js
My controllers are in the following directory
app/js/controllers/
Here is my karma.conf.js file
files: [
'Scripts/jquery-2.1.1.min.js',
'Scripts/require.js',
'Scripts/angular.js',
'Scripts/angular-mocks.js',
{ pattern: 'app/js/*.js', included: false },
{ pattern: 'app/js/**/*.js', included: false },
{ pattern: 'app/js/**/**/*.js', included: false },
{ pattern: 'app/js/**/**/**/*.js', included: false },
{ pattern: 'test/specs/**/*.js', included: false },
'test/test-main.js',
],
// list of files to exclude
exclude: [
'app/js/main.js'
],
test-main.js
var testFiles = [];
for (var file in window.__karma__.files) {
console.log(file);
if (/Spec\.js$/.test(file)) {
testFiles.push(file);
}
}
requirejs.config({
paths: {
// External libraries
'jquery': '../../Scripts/jquery-2.1.1.min',
'angular': '../../Scripts/angular',
'angular-mocks': '../../Scripts/angular-mocks',
'ngRoute': '../../Scripts/angular-route.min',
'angular-animate': '../../Scripts/angular-animate.min',
'angular-cookies': '../../Scripts/angular-cookies.min',
},
baseUrl: '/base/app/js',
shim: {
'angular': {
exports: 'angular',
deps: ['jquery']
},
'angular-mocks': { exports: 'angular-mocks', deps: ['angular'] },
'angular-animate': { exports: 'angular-animate', deps: ['angular'] },
'ngRoute': { exports: 'ngRoute', deps: ['angular'] },
'angular-cookies': { exports: 'angular-cookies', deps: ['angular'] },
},
// dynamically load all test files
deps: testFiles,
// we have to kickoff jasmine, as it is asynchronous
callback: window.__karma__.start
});
I added this line...
console.log(file)
To make sure the file was loaded into
window.__karma__.files
and it is.
The test lives in test/specs/
define(['angular', 'angular-mocks'], (angular, ngMock: ng.IMockStatic) => {
var module = ngMock.module;
var inject: (...fns: Function[]) => any = ngMock.inject;
describe("Create an Application", function () {
var scope, q, routeParams;
var location;
var app;
beforeEach(function () {
app = angular.module('App', []);
inject(function ($rootScope, $q: ng.IQService, $location: ng.ILocationService) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
});
My app file looks like this....
import angular = require('angular');
import angularRoute = require('angular-route');
import angularAnimate = require('angular-animate');
import ds = require('services/DataService');
var app = angular.module('app', [
'ngRoute',
'ngAnimate',
'ngCookies',
'kendo.directives',
'breeze.angular',
'ui.bootstrap'
]);
export = app;
the error that i get when I try to run the test is
failed to instantiate module app due to: Module 'app' is not available! You either misspelled the module name or forgot to load it.
I am assuming it is not loading but not sure how I can tell. Is this the best way to accomplish my testing?
Any help is greatly appreciated!
Thanks so much!
If you are using Karma with Jasmine, you may be having the problem of Jasmine being too eager.
This can be fixed by making two changes, originally described in an article I wrote when I was doing a similar thing, Combining TypeScript, Jasmine and RequireJS.
In boot.js you need to comment out the env.execute(); method call:
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
htmlReporter.initialize();
//env.execute();
};
This is because the window.onload event can happen before require.js has loaded everything.
Once you have loaded the modules you need, you can then call:
// Hai Jasmine - ready to go!
jasmine.getEnv().execute();
This is what it gets compiled with your code and the -m amd switch
describe("Create an Application", function () {
var scope, q, routeParams;
var controller, location;
var route;
beforeEach(function () {
module('app', ['ngRoute', 'ngAnimate', 'ngCookies', 'kendo.directives', 'breeze.angular', 'ui.bootstrap']);
inject(function ($rootScope, $q, $location) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
// controller = new controller.ApplicationService(q, scope, route, location, _datasvc, _searchsvc);
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
is not a module, and its synchronous
But. in a module works
define(['angular','angular-mocks'],(angular,ngMock:ng.IMockStatic)=>{
var module = ngMock.module;
var inject: (...fns: Function[])=>any = ngMock.inject;
describe("Create an Application", function () {
var scope, q, routeParams;
var location;
var app;
beforeEach(function () {
app = angular.module('App', []);
inject(function ($rootScope, $q: ng.IQService, $location: ng.ILocationService) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
});
Or alternatively in a more TypeScripty w/ES6esque way :
import ngMock = require('angular-mocks');
import angular = require('angular');
var module = ngMock.module;
var inject: (...fns: Function[])=>any = ngMock.inject;
describe("Create an Application", function () {
var scope, q, routeParams;
var location;
beforeEach(function () {
var app = angular.module('app', []);
inject(function ($rootScope, $q: ng.IQService, $location: ng.ILocationService) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
check that you are loading angular as in a script tag
'files:[Scripts/angular.js',... ,
that's why you get
WARNING:tried load angular more than once
Put it in a { pattern: 'Scripts/angular.js', included: false ...],
you need to work out the requirejs.confg until the deps chain are satisfied. in order., and thats one of the reasons why you set the shim config with
'angular-mocks': { exports: 'angular-mocks', deps: ['angular'] },
so when requiring angular-mocks angular is required too ,
have a look at [this github repo](https://github.com/tnajdek/angular-requirejs-
seed),
...And once you have an instance attached to angular.module /mock.module in your spec , check Steve's answer again ,
If you can't find boot.js , you might be using jasmine bundled with the Karma adapter. in that case it should work given you have sorted out the require test-maiin.js / config / "NG_DEFER_BOOTSTRAP!" /resumeBootstrap story