title: Angular Karma/Jasmine Unit Test not working
body: <p>I'm trying to write unit tests for my new Angular App and have troubles. Below is my controller.</p>
  
  <pre><code>'use strict';
  
  angular.module('nileLeApp')
  .controller('RegisterController', function ($scope, $translate, $timeout, vcRecaptchaService, Auth, Country, Timezone, RecaptchaService) {
      $scope.success = null;
      $scope.error = null;
      $scope.doNotMatch = null;
      $scope.errorUserExists = null;
      $scope.registerAccount = {};
      $timeout(function () {
          angular.element('[ng-model="registerAccount.email"]').focus();
      });
  
      $scope.loadCountries = function () {
          Country.getCountries()
              .then(function (result) {
                  $scope.countries = result.data;
              });
      };
  
      $scope.loadTimezones = function () {
          Timezone.getTimezones()
              .then(function (result) {
                  $scope.timezones = result.data;
              });
      };
  
      // ============ Recaptcha specific code START ===============
      $scope.recaptcha = {};
      $scope.recaptcha.recaptchaResponse = null;
      $scope.recaptchaWidgetId = null;
  
      $scope.setResponse = function (response) {
          $scope.recaptcha.recaptchaResponse = response;
          $scope.recaptchaMissing = false;
      };
      $scope.setWidgetId = function (widgetId) {
          $scope.recaptchaWidgetId = widgetId;
      };
      $scope.cbExpiration = function () {
          $scope.recaptcha.recaptchaResponse = null;
      };
      // ============ Recaptcha specific code END ===============
  
      $scope.createAccount = function () {
          Auth.createAccount($scope.registerAccount).then(function (response) {
              $scope.success = true;
          }).catch(function (response) {
              $scope.success = false;
          });
      }
  
      $scope.register = function () {
  
          $scope.recaptchaMissing = false;
          $scope.recaptchaInvalid = false;
  
          if ($scope.recaptcha.recaptchaResponse != null) {
              RecaptchaService.verify($scope.recaptcha).$promise
                  .then(function (response) {
                      if (response.data) {
                          $scope.createAccount();
                      } else {
                          $scope.recaptchaInvalid = true;
                          vcRecaptchaService.reload($scope.recaptchaWidgetId); // Reload captcha
                      }
                  }).catch(function (response) {
              });
          } else {
              $scope.recaptchaMissing = true;
          }
      };
  
      $scope.loadCountries();
      $scope.loadTimezones();
  });
  </code></pre>
  
  <p>Below is the test I'm trying.</p>
  
  <pre><code>'use strict';
  
  describe('Register Controllers Tests', function () {
  
  describe('RegisterController', function () {
  
      // actual implementations
      var $scope;
      var $q;
      // mocks
      var MockTimeout;
      var MockTranslate;
      var MockAuth;
      var MockCountry;
      var MockTimezone;
      // local utility function
      var createController;
  
      beforeEach(inject(function ($injector) {
          $q = $injector.get('$q');
          $scope = $injector.get('$rootScope').$new();
          MockTimeout = jasmine.createSpy('MockTimeout');
          MockAuth = jasmine.createSpyObj('MockAuth', ['createAccount']);
          MockCountry = jasmine.createSpyObj('MockCountry', ['getCountries']);
          MockTimezone = jasmine.createSpyObj('MockTimezone', ['getTimezones']);
          MockTranslate = jasmine.createSpyObj('MockTranslate', ['use']);
  
  
          var locals = {
              '$scope': $scope,
              '$translate': MockTranslate,
              '$timeout': MockTimeout,
              'Auth': MockAuth,
              'Country': MockCountry,
              'Timezone': MockTimezone
          };
          createController = function () {
              $injector.get('$controller')('RegisterController', locals);
          };
      }));
  
      it('should load countries on page load', function () {
  
          var mockCountryResponse = [{
              'countryId': 1,
              'alpha2Code': "AF",
              'countryName': "Afghanistan"
          }];
  
          MockCountry.getCountries.and.returnValue($q.resolve(mockCountryResponse));
          MockTimezone.getTimezones.and.returnValue($q.resolve());
          MockAuth.createAccount.and.returnValue($q.resolve());
  
          // given
          createController();
  
          $scope.$apply($scope.loadCountries);
          expect($scope.countries).toEqual(mockCountryResponse);
      });
  
  });
  </code></pre>
  
  <p>The above expectation doesn't work because $scope.countries is undefined. Following is the error message.</p>
  
  <pre><code>TypeError: 'undefined' is not an object (evaluating 'result.data')
  </code></pre>
  
  <p>Also, I see the test getting called twice for some strange reason. Below is my Karma configuration file.</p>
  
  <pre><code>// Karma configuration
  // http://karma-runner.github.io/0.10/config/configuration-file.html
  
  module.exports = function (config) {
  config.set({
      // base path, that will be used to resolve files and exclude
      basePath: '../../',
  
      // testing framework to use (jasmine/mocha/qunit/...)
      frameworks: ['jasmine'],
  
      // list of files / patterns to load in the browser
      files: [
          // bower:js
          'main/webapp/bower_components/es5-shim/es5-shim.js',
          'main/webapp/bower_components/jquery/dist/jquery.js',
          'main/webapp/bower_components/angular/angular.js',
          'main/webapp/bower_components/angular-animate/angular-animate.js',
          'main/webapp/bower_components/angular-aria/angular-aria.js',
          'main/webapp/bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
          'main/webapp/bower_components/bootstrap/dist/js/bootstrap.js',
          'main/webapp/bower_components/angular-bootstrap-nav-tree/dist/abn_tree_directive.js',
          'main/webapp/bower_components/angular-file-upload/angular-file-upload.js',
          'main/webapp/bower_components/angular-messages/angular-messages.js',
          'main/webapp/bower_components/skycons/skycons.js',
          'main/webapp/bower_components/angular-skycons/angular-skycons.js',
          'main/webapp/bower_components/angular-smart-table/dist/smart-table.min.js',
          'main/webapp/bower_components/angular-touch/angular-touch.js',
          'main/webapp/bower_components/angular-cache-buster/angular-cache-buster.js',
          'main/webapp/bower_components/angular-cookies/angular-cookies.js',
          'main/webapp/bower_components/angular-dynamic-locale/src/tmhDynamicLocale.js',
          'main/webapp/bower_components/angular-local-storage/dist/angular-local-storage.js',
          'main/webapp/bower_components/angular-loading-bar/build/loading-bar.js',
          'main/webapp/bower_components/angular-resource/angular-resource.js',
          'main/webapp/bower_components/angular-sanitize/angular-sanitize.js',
          'main/webapp/bower_components/angular-translate/angular-translate.js',
          'main/webapp/bower_components/messageformat/messageformat.js',
          'main/webapp/bower_components/angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.js',
          'main/webapp/bower_components/angular-translate-loader-partial/angular-translate-loader-partial.js',
          'main/webapp/bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js',
          'main/webapp/bower_components/angular-translate-storage-cookie/angular-translate-storage-cookie.js',
          'main/webapp/bower_components/angular-translate-storage-local/angular-translate-storage-local.js',
          'main/webapp/bower_components/angular-ui-router/release/angular-ui-router.js',
          'main/webapp/bower_components/moment/moment.js',
          'main/webapp/bower_components/fullcalendar/dist/fullcalendar.js',
          'main/webapp/bower_components/angular-ui-calendar/src/calendar.js',
          'main/webapp/bower_components/angular-ui-grid/ui-grid.js',
          'main/webapp/bower_components/angular-ui-select/dist/select.js',
          'main/webapp/bower_components/angular-ui-utils/ui-utils.js',
          'main/webapp/bower_components/angular-xeditable/dist/js/xeditable.js',
          'main/webapp/bower_components/angularjs-toaster/toaster.js',
          'main/webapp/bower_components/angular-strap/dist/angular-strap.js',
          'main/webapp/bower_components/angular-strap/dist/angular-strap.tpl.js',
          'main/webapp/bower_components/angular-recaptcha/release/angular-recaptcha.js',
          'main/webapp/bower_components/bootstrap-daterangepicker/daterangepicker.js',
          'main/webapp/bower_components/bootstrap-filestyle/src/bootstrap-filestyle.js',
          'main/webapp/bower_components/bootstrap-slider/bootstrap-slider.js',
          'main/webapp/bower_components/bootstrap-tagsinput/dist/bootstrap-tagsinput.js',
          'main/webapp/bower_components/bootstrap-wysiwyg/bootstrap-wysiwyg.js',
          'main/webapp/bower_components/bower-jvectormap/jquery-jvectormap-1.2.2.min.js',
          'main/webapp/bower_components/datatables/media/js/jquery.dataTables.js',
          'main/webapp/bower_components/flot/jquery.flot.js',
          'main/webapp/bower_components/flot-spline/js/jquery.flot.spline.js',
          'main/webapp/bower_components/flot.tooltip/js/jquery.flot.tooltip.js',
          'main/webapp/bower_components/footable/js/footable.js',
          'main/webapp/bower_components/html5sortable/jquery.sortable.js',
          'main/webapp/bower_components/json3/lib/json3.js',
          'main/webapp/bower_components/ng-grid/build/ng-grid.js',
          'main/webapp/bower_components/intl-tel-input/build/js/intlTelInput.min.js',
          'main/webapp/bower_components/intl-tel-input/lib/libphonenumber/build/utils.js',
          'main/webapp/bower_components/ng-intl-tel-input/dist/ng-intl-tel-input.js',
          'main/webapp/bower_components/ngImgCrop/compile/minified/ng-img-crop.js',
          'main/webapp/bower_components/ngstorage/ngStorage.js',
          'main/webapp/bower_components/ng-file-upload/ng-file-upload.js',
          'main/webapp/bower_components/ngInfiniteScroll/build/ng-infinite-scroll.js',
          'main/webapp/bower_components/oclazyload/dist/ocLazyLoad.min.js',
          'main/webapp/bower_components/screenfull/dist/screenfull.js',
          'main/webapp/bower_components/slimscroll/jquery.slimscroll.min.js',
          'main/webapp/bower_components/textAngular/dist/textAngular.min.js',
          'main/webapp/bower_components/venturocket-angular-slider/build/angular-slider.js',
          'main/webapp/bower_components/videogular/videogular.js',
          'main/webapp/bower_components/videogular-buffering/buffering.js',
          'main/webapp/bower_components/videogular-controls/controls.js',
          'main/webapp/bower_components/videogular-ima-ads/ima-ads.js',
          'main/webapp/bower_components/videogular-overlay-play/overlay-play.js',
          'main/webapp/bower_components/videogular-poster/poster.js',
          'main/webapp/bower_components/waves/dist/waves.min.js',
          'main/webapp/bower_components/angular-mocks/angular-mocks.js',
          // endbower
          'main/webapp/scripts/app/app.js',
          'main/webapp/scripts/app/**/*.+(js|html)',
          'main/webapp/scripts/components/**/*.+(js|html)',
          'test/javascript/spec/helpers/module.js',
          'test/javascript/spec/helpers/httpBackend.js',
          'test/javascript/**/!(karma.conf|protractor.conf).js'
      ],
  
  
      // list of files / patterns to exclude
      exclude: ['test/javascript/e2e/**'],
  
      preprocessors: {
          './main/webapp/scripts/**/*.js': ['coverage'],
          '**/*.html': ['ng-html2js']
      },
  
      reporters: ['dots', 'jenkins', 'coverage', 'progress'],
  
      jenkinsReporter: {
  
          outputFile: '../build/test-results/karma/TESTS-results.xml'
      },
  
      coverageReporter: {
  
          dir: '../build/test-results/coverage',
          reporters: [
              {type: 'lcov', subdir: 'report-lcov'}
          ]
      },
  
      // web server port
      port: 9876,
  
      // level of logging
      // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
      logLevel: config.LOG_INFO,
  
      // enable / disable watching file and executing tests whenever any file changes
      autoWatch: false,
  
      // Start these browsers, currently available:
      // - Chrome
      // - ChromeCanary
      // - Firefox
      // - Opera
      // - Safari (only Mac)
      // - PhantomJS
      // - IE (only Windows)
      browsers: ['PhantomJS'],
  
      // Continuous Integration mode
      // if true, it capture browsers, run tests and exit
      singleRun: true,
  
      // to avoid DISCONNECTED messages when connecting to slow virtual machines
      browserDisconnectTimeout : 10000, // default 2000
      browserDisconnectTolerance : 1, // default 0
      browserNoActivityTimeout : 4*60*1000 //default 10000
  });
  };
  </code></pre>
  
  <p>I'm stuck on writing unit test for last couple days as I find them quite confusing and not simple like in Java. Will appreciate any help.</p>