Reputation: 2183
I've seen this answered but none of the solutions are working for me. I'm getting an error Error: [ng:areq] Argument 'fn' is not a function, got undefined
which is causing my test to fail. What's the correct way to inject a factory into a test spec that isn't a stubbed factory.
login module
angular.module('loginModule', ['ui.bootstrap', 'permission', 'ui.router', 'coreModule', 'utilsModule']);
login controller
(function(){
'use strict';
angular.module('loginModule')
.controller('loginController', login);
login.$inject = [
'$log',
'$uibModal',
'$rootScope',
'storageFactory',
'loginFactory',
'$state',
'RoleStore',
'PermissionStore',
'vsmsCoreFactory'
];
function login($log, $uibModal, $rootScope, storageFactory, loginFactory, $state, RoleStore, PermissionStore, vsmsCoreFactory) {
/* jshint validthis: true */
var vm = this;
vm.loginUser = loginUser;
vm.forgotPassword = forgotPassword;
vm.errorCode = null;
PermissionStore.clearStore();
vsmsCoreFactory.getHashFunction()
.then(function(response) {
if(response) {
storageFactory.setHashFunction(response); // unknown provider
}
});
function loginUser() {
...
login controller spec
describe('loginController', function() {
var $controller;
beforeEach(module('loginModule'));
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('vm.loginUser', function() {
it('should be defined', function() {
var loginController = $controller('loginController');
expect(loginController.loginUser).toBeDefined();
});
});
});
unit test files
'use strict';
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var wiredep = require('wiredep');
var paths = gulp.paths;
function runTests (singleRun, done) {
var bowerDeps = wiredep({
directory: 'bower_components',
exclude: ['bootstrap-sass-official'],
dependencies: true,
devDependencies: true
});
var testFiles = bowerDeps.js.concat([
'./src/components/scripts/ui-bootstrap-custom-tpls-2.1.3.js',
'./src/app/index.js',
'./src/{app,components}/**/*.module.js',
'./src/{app,components}/**/*.factory.js',
'./src/{app,components}/**/*.controller.js',
'./src/{app,components}/**/*.spec.js'
]);
gulp.src(testFiles)
.pipe($.karma({
configFile: 'karma.conf.js',
action: (singleRun)? 'run': 'watch'
}))
.on('error', function (err) {
// Make sure failed tests cause gulp to exit non-zero
throw err;
});
}
gulp.task('test', function (done) { runTests(true /* singleRun */, done) });
gulp.task('test:auto', function (done) { runTests(false /* singleRun */, done) });
app
'use strict';
angular.module('fotaAdminPortal',
[
'ngAnimate',
'ngCookies',
'ngTouch',
'ngSanitize',
'ngResource',
'ui.bootstrap',
'ui.router',
'ui.router.stateHelper',
'pascalprecht.translate',
'utilsModule',
'loginModule',
...
])
log
Chrome 54.0.2840 (Mac OS X 10.11.6) loginController vm.loginUser should be defined FAILED Error: [$injector:unpr] Unknown provider: storageFactoryProvider <- storageFactory <- loginController
Upvotes: 2
Views: 1235
Reputation: 6759
The fotaAdminPortal
module isn't loaded for the test so vsmsCoreFactory
never gets registered.
Also there's no need to manually pass services into a controller as the injector will automatically do this when you create it. Passing services manually is useful when you need to pass something that isn't a globally registered service ie $scope
or mock a service for a single controller instance.
Atm the mocks for the factories are just an undefined variable which will cause errors when the logic depending on them tries to call the methods that don't exist. You'll need to properly stub any methods on these mocks. But leaving those aside for the moment your test should be something like:
describe('loginController', function() {
var $controller;
beforeEach(module('fotaAdminPortal'));
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('vm.loginUser', function() {
it('should be defined', function() {
var loginController = $controller('loginController');
expect(loginController.loginUser).toBeDefined();
});
});
});
If you want to load loginModule
in isolation then you need to extract vsmsCoreFactory
into another module and add that as a dependency:
angular.module('vsmsCore', [])
.factory('vsmsCoreFactory', function($http, env, $log, storageFactory) {
});
angular.module('loginModule', ['ui.bootstrap', 'permission', 'ui.router', 'vsmsCore']);
Then you can do beforeEach(module('loginModule'))
instead
Upvotes: 1