2016-02-05 105 views
1

我正在尝试为我的新角度应用程序编写单元测试并遇到麻烦。以下是我的控制器。角度Karma/Jasmine单元测试不起作用

'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(); 
}); 

以下是我正在尝试的测试。

'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); 
    }); 

}); 

上述期望不起作用,因为$ scope.countries未定义。以下是错误消息。

TypeError: 'undefined' is not an object (evaluating 'result.data') 

此外,我看到测试被调用两次出于某种奇怪的原因。以下是我的Karma配置文件。

// 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 
}); 
}; 

我被困在写单元测试最后几天,因为我发现它们很混乱,并不像Java那样简单。将感谢任何帮助。

回答

1

上述预期不起作用,因为$ scope.countries未定义

^^这是不正确的。这并不是说$ scope.countries是不确定的,那就是result是不确定的,并且它不是result你想分配给$scope.countries,这是有关$scope.timezones

我觉得一个这是你的问题就在这里:

MockTimezone.getTimezones.and.returnValue($q.resolve()); 

你隐式地将未定义的函数传递给那个函数,并且当你实例化你的控制器时抛出一个错误。 它抛出的错误,因为你已经有了这条线在你的控制器的末尾:

$scope.loadTimezones(); 

这是出于这个原因,我停止了自己内初始化控制器。现在我使用ng-init从HTML启动。如果你像我一样做出同样的改变,你将来再也不会遇到这样的问题。

+0

这很有道理。试图解决这个问题。你知道为什么测试运行两次吗? – TechCrunch