neridaj
neridaj

Reputation: 2183

Angular Unit Testing Factory Injection

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

Answers (1)

noj
noj

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

Related Questions