2

我正在尝试在使用 Yeoman 搭建的 AngularJS 应用程序中运行单元测试。我尝试运行的唯一测试只是检查数组是否具有正确的长度但由于与 CanvasJS 相关的某些原因而失败

这里的日志

LOG: 'CanvasJS Error: Chart Container with id "chartContainer" was not found'
PhantomJS 1.9.7 (Windows 7) Controller: MainCtrl should attach a list of inputs to the scope  FAILED
        TypeError: 'undefined' is not an object (evaluating 'this._toolBar.appendChild')
            at /path/to/Chart/app/scripts/vendor/canvasjs-1.5.0-beta/source/canvasjs.js:1799
            at /path/to/Chart/app/scripts/vendor/canvasjs-1.5.0-beta/source/canvasjs.js:1919
            at /path/to/Chart/app/scripts/vendor/canvasjs-1.5.0-beta/source/canvasjs.js:2093
            at /path/to/Chart/app/scripts/vendor/canvasjs-1.5.0-beta/source/canvasjs.js:12551
            at /path/to/Chart/app/scripts/controllers/main.js:101
            at invoke (/path/to/Chart/app/bower_components/angular/angular.js:3869)
            at instantiate (/path/to/Chart/app/bower_components/angular/angular.js:3880)
            at /path/to/Chart/app/bower_components/angular/angular.js:7134
            at /path/to/Chart/test/spec/controllers/main.js:15
            at invoke (/path/to/Chart/app/bower_components/angular/angular.js:3869)
            at workFn (/path/to/Chart/app/bower_components/angular-mocks/angular-mocks.js:2147)
        undefined
        Expected 0 to be 3.
PhantomJS 1.9.7 (Windows 7): Executed 1 of 1 (1 FAILED) ERROR (0.025 secs / 0.021 secs)
Warning: Task "karma:unit" failed. Use --force to continue.

该应用程序可以在这里找到https://github.com/kaiohken1982/Chart

我不确定我是否在 AngularJS 应用程序中正确实现了这个库,我们将不胜感激。

谢谢

------------------- 编辑 #1 --------------------

下面的一些代码可以更好地理解场景,正如您所看到的,CanvasJS 被用作全局而不是作为服务注入,所以问题之一是如果没有凉亭回购,以这种方式使用第三方库是否正确.

我认为这条线导致了这个问题

...
$scope.chart = new CanvasJS.Chart('chartContainer', {
...

控制器视图包含“chartContainer” id

<div id="chartContainer"></div>

app/scripts/controllers/main.js '使用严格';

/*global CanvasJS:false */

/**
 * @ngdoc function
 * @name ngprojectApp.controller:MainCtrl
 * @description
 * # MainCtrl
 * Controller of the ngprojectApp
 */
angular.module('ChartApp')
  .controller('MainCtrl', function ($scope) {

    // Regexp to ensure that input dates are in the required format
    $scope.dateRegexp = /^(January|February|March|April|June|July|August|Semptember|October|November|December)[ ](0[1-9]|1[0-9]|2[0-9]|3[0-1])[ ](19|20)\d{2}$/i;

    // Called to remove an entry
    $scope.removeInput = function (index) {
      $scope.inputs.splice(index, 1);
    };

    // Called to add an entry
    $scope.addRow = function () {
      $scope.inputs.push($scope.value);
      $scope.value = '';
    };

     $scope.predicate = '';
     $scope.reverse = false;
     $scope.inputs = [];
     $scope.updateMyText = function() {};

     // Chart below
     var datapoints = [];

     var chartPoint = null;
     $scope.canEdit = true;
     $scope.editMode = false;
     $scope.units = 1; // increase or decrease bar value by this unit
     $scope.chart = new CanvasJS.Chart('chartContainer', {
       theme: 'theme1',
       interactivityEnabled: true,
       title:{
          text: 'Website response time'              
       },
       axisY: {
          title: 'ms',
          labelFontSize: 16,
       },
       axisX: {
          title: 'timeline',
          labelFontSize: 16,
          gridThickness: 1
       },
       data: [              
          {
            click: function() {
              $scope.$apply(function() {
                if($scope.canEdit) {
                  $scope.editMode = !$scope.editMode; 
                }
              });

              if($scope.editMode) {
                console.log('EditMode is now TRUE');
              }

              if(!$scope.editMode) {
                console.log('EditMode is now FALSE');
              }
            },
            mousemove: function(e) {
              if(!$scope.editMode) {
                chartPoint = null;
                return false;
              }

              // First point? Assign it and return
              if(null === chartPoint) {
                chartPoint = e;
                return false;
              }

              // Update inputs at the correct index will re-render graph and table data
              $scope.$apply(function() {
                var diffY = e.y - chartPoint.y; // if it is > 0 means that the mouse pointer went UP
                var time = diffY < 0 ? e.dataPoint.y+$scope.units : e.dataPoint.y-$scope.units;
                $scope.inputs[e.dataPointIndex].time = time;
                // Update chartPoint
                chartPoint = e;
              });

            },
             type: 'column',
             dataPoints: []
          }
       ]
    });

    $scope.chart.render(); //render the chart for the first time

    $scope.changeChartType = function(chartType) {
      $scope.canEdit = 'column' === chartType;
      $scope.editMode = false; // disable EditMode whatever the type is
      $scope.chart.options.data[0].dataPoints = datapoints;
      $scope.chart.options.data[0].type = chartType;
        $scope.chart.render(); //re-render the chart to display the new layout
    };

    // DEEP watch for any changes in the array
    $scope.$watch('inputs', function() {
      datapoints = [];
      for(var i in $scope.inputs) {
        var e = $scope.inputs[i];
        // update the timestamp for the value
        var d = new Date(e.date);
        e.timestamp = d.valueOf() / 1000;
        datapoints.push({
          'label': e.url,
          x: new Date(e.date),
          y: e.time
        });
      }
      // Reassign data to chart
      $scope.chart.options.data[0].dataPoints = datapoints;
      $scope.chart.render();
    }, true);


    $scope.inputs.push({
      date: 'January 02 2014',
      url: 'http://uno.it',
      time: 111
    });

    $scope.inputs.push({
      date: 'January 12 2014',
      url: 'http://due.it',
      time: 292
    });

    $scope.inputs.push({
      date: 'January 22 2014',
      url: 'http://tre.it',
      time: 333
    });

  });

test/spec/controllers/main.js '使用严格';

describe('Controller: MainCtrl', function () {

  // load the controller's module
  beforeEach(module('ChartApp'));

  var MainCtrl,
    scope;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    MainCtrl = $controller('MainCtrl', {
      $scope: scope
    });
  }));

  it('should attach a list of inputs to the scope ', function () {
    expect(scope.inputs.length).toBe(3);
  });
});

Karma.conf.js 有所有必需的文件,我想......

....
    files: [
      'app/bower_components/angular/angular.js',
      'app/bower_components/angular-mocks/angular-mocks.js',
      'app/bower_components/angular-animate/angular-animate.js',
      'app/bower_components/angular-cookies/angular-cookies.js',
      'app/bower_components/angular-resource/angular-resource.js',
      'app/bower_components/angular-route/angular-route.js',
      'app/bower_components/angular-sanitize/angular-sanitize.js',
      'app/bower_components/angular-touch/angular-touch.js',

      'app/bower_components/jquery/dist/jquery.js',
      'app/bower_components/angular-ui/build/angular-ui.js',
      'app/bower_components/jquery-ui/jquery-ui.js',
      'app/scripts/vendor/canvasjs-1.5.0-beta/canvasjs.min.js',

      'app/scripts/directives/directive.js',
      'app/scripts/**/*.js',
      'test/mock/**/*.js',
      'test/spec/**/*.js'
    ],
...

------------------- 编辑 #2 --------------------

这就是我在收到建议后编辑 main.js 测试的方式

  beforeEach(inject(function ($controller, $rootScope, $compile) {
    var htmlString = '' +
    '<div ng-controller="MainCtrl">' +
    '    <div id="chartContainer">' +
    '</div>'
    ;
    var element = angular.element(htmlString)
    scope = $rootScope.$new();
    $compile(element)(scope);
    scope.$digest();
    MainCtrl = $controller('MainCtrl', {
      $scope: scope
    });
  }));

错误几乎一样

LOG: 'CanvasJS Error: Chart Container with id "chartContainer" was not found'
PhantomJS 1.9.7 (Windows 7) Controller: MainCtrl should attach a list of inputs to the scope  FAILED
        TypeError: 'undefined' is not an object (evaluating 'this._toolBar.appendChild')
            at /path/to/workspace/Chart/app/scripts/vendor/canvasjs-1.5.0-beta/source/canvasjs.js:1799
            at /path/to/workspace/Chart/app/scripts/vendor/canvasjs-1.5.0-beta/source/canvasjs.js:1919
            at /path/to/workspace/Chart/app/scripts/vendor/canvasjs-1.5.0-beta/source/canvasjs.js:2093
            at /path/to/workspace/Chart/app/scripts/vendor/canvasjs-1.5.0-beta/source/canvasjs.js:12551
            at /path/to/workspace/Chart/app/scripts/controllers/main.js:101
            at invoke (/path/to/workspace/Chart/app/bower_components/angular/angular.js:3869)
            at instantiate (/path/to/workspace/Chart/app/bower_components/angular/angular.js:3880)
            at /path/to/workspace/Chart/app/bower_components/angular/angular.js:7134
            at /path/to/workspace/Chart/app/bower_components/angular/angular.js:6538
            at forEach (/path/to/workspace/Chart/app/bower_components/angular/angular.js:330)
            at nodeLinkFn (/path/to/workspace/Chart/app/bower_components/angular/angular.js:6552)
            at compositeLinkFn (/path/to/workspace/Chart/app/bower_components/angular/angular.js:5986)
            at publicLinkFn (/path/to/workspace/Chart/app/bower_components/angular/angular.js:5891)
            at /path/to/workspace/Chart/test/spec/controllers/main.js:20
            at invoke (/path/to/workspace/Chart/app/bower_components/angular/angular.js:3869)
            at workFn (/path/to/workspace/Chart/app/bower_components/angular-mocks/angular-mocks.js:2147)
        undefined
        TypeError: 'undefined' is not an object (evaluating 'scope.inputs.length')
            at /path/to/workspace/Chart/test/spec/controllers/main.js:28
PhantomJS 1.9.7 (Windows 7): Executed 1 of 1 (1 FAILED) ERROR (0.029 secs / 0.028 secs)
Warning: Task "karma:unit" failed. Use --force to continue.

更新测试控制器时我是否弄错了什么?

------------------- 编辑#3 --------------------

在 Ivarni 的宝贵帮助下,我最终得到了这个解决方案:

在应用程序控制器中

...
$scope.chart = new $window.CanvasJS.Chart('chartContainer', {
...

在控制器测试中

...
beforeEach(inject(function ($controller, $rootScope, $compile, $window) {
    $window.CanvasJS = { Chart: function(){
        this.render = function() {};
    } };
...

现在可以模拟 CanvasJS 并且可以运行 UnitTest :)

4

1 回答 1

0

我认为您对导致错误的原因的假设是正确的。您的控制器未附加到 DOM,因此它无法找到具有 id “chartContainer”的任何内容是合理的。

对于单元测试,我会尝试与单元测试指令相同的方法。这是类似的东西

beforeEach(inject(function($rootScope, $compile) {
    var htmlString = '' +
        '<div ng-controller="MainCtrl">' +
        '    <div id="chartContainer">' +
        '</div>'
        ;
    element = angular.element(htmlString)
    scope = $rootScope.$new();
    $compile(element)(scope);
    scope.$digest();
}));

这编译了一个最小的 DOM 片段,附加了你的控制器和一个带有 CanvasJS 所需 id 的元素。你可能不得不更多地摆弄它。只要您确保将 CanvasJS 包含在 karma 配置中,我认为使用 CanvasJS 作为全局没有问题。

另一种探索途径是查看 Angular 的 e2e 测试功能。这里有一些文档。当您测试 DOM 渲染时,这可能更合适。

于 2014-08-21T05:59:21.697 回答