【问题标题】:How to unit test angularjs form?如何对angularjs表单进行单元测试?
【发布时间】:2014-09-21 06:13:09
【问题描述】:

我一直在学习 AngularJS,并且在单元测试方面进展非常顺利,但我遇到了一个棘手的问题。

假设我有一个简单的表格,例如:

<form name="form">
    <input type="text" name="number" ng-pattern="/^d+$/">
</form>

如果我在测试控制器之类的东西,我知道我会写成这样(使用 Jasmine + Karma):

beforeEach(module('some.module'));

beforeEach(inject(/* services */) {
    /* inject necessary services */
});

it('should be invalid when given bad input', function () {
    form.number = 'Not a number';
    expect(form.number.$valid).toBeFalsy();
    expect(form.$valid).toBeFalsy();
});

但我不知道我需要注入哪些服务,而且我没有任何运气在the forms guide 或the ng-form documentation 中找到有关单元测试的文档。

如何在 Angular 中对表单进行单元测试?

【问题讨论】:

  • 你试过注入持有表单逻辑的控制器吗?
  • 现在,表单验证由像ng-pattern这样的普通角度指令处理。如果我确实将控制器注入到我的测试中,它似乎没有对form 的任何引用(范围也没有)。
  • 您不对表单进行单元测试,而是对处理它的控制器进行单元测试。要测试表单是否匹配 ng-pattern 和其他类似的东西,请使用 e2e 测试。
  • 假设我想对处理表单的控制器进行单元测试,我将如何将当前嵌入在表单中的逻辑移动到控制器中?

标签: javascript angularjs unit-testing jasmine karma-runner


【解决方案1】:

我不认为这是对此类内容进行单元测试的最佳方法,但在 this answer on testing custom angular directives 的帮助和一些实验的帮助下,我找到了一种对表单进行单元测试的方法。

在安装karma-ng-html2js-preprocessor 并对其进行配置后,我设法得到了一个这样的工作单元测试:

var scope, form;

beforeEach(function() {
  module('my-module');
  module('templates');
});

beforeEach(inject($rootScope, $controller, $templateCache, $compile) {
    scope = $rootScope.$new()

    ctrl = $controller('MyController'), {
        "$scope": scope
    }

    templateHtml = $templateCache.get('path/to/my/template.html')
    formElem = angular.element("<div>" + templateHtml + "</div>")
    $compile(formElem)(scope)
    form = scope.form

    scope.$apply()
}

it('should not allow an invalid `width`', function() {
  expect(form.$valid).toBeTruthy();
  form.number.$setViewValue('BANANA');
  expect(form.number.$valid).toBeFalsy()
});

【讨论】:

  • 这似乎对我不起作用。编译工作正常并且控制器启动,但是当范围内发生更改并且我运行摘要时,角度会向我抛出一个错误 createElement("div") is not a function。
【解决方案2】:

我想我可以在接受的答案中添加一些细节:karma-ng-html2js-preprocessor 应该以类似的方式在 karma.conf.js 文件中配置:

//karma.conf.js
ngHtml2JsPreprocessor: { 
    moduleName: 'templates'
},
files: [
    //... other files
    //my templates 
    'app/**/*.html'
],
preprocessors: {
    'app/**/*.html': ['ng-html2js']
}, 
plugins: [
    //... other plugins
    "karma-ng-html2js-preprocessor"
]

【讨论】:

    【解决方案3】:

    这是一种无需编译控制器模板即可使用角度形式进行单元测试的方法。在我有限的使用中很适合我。

    describe('Test', function() {
    
      var $scope, fooController;
    
      beforeEach(function($rootScope, $controller, formDirective) {
    
        $scope = $rootScope.$new();
        fooController = $controller('fooController', {$scope: $scope});
    
        // we manually create the form controller
        fooController.form = $controller(formDirective[0].controller, {
          $scope: $scope,
          $element: angular.element("<form></form>"),
          $attrs: {}
        });
    
      });
    
      it('should test something', function() {
        expect(fooController.form.$valid).toBeFalsy();
      });
    
    });
    

    【讨论】:

      【解决方案4】:

      或者,如果您使用 WebPack 和 karma-webpack - 您可以使用 require 包含模板,而无需 karma-ng-html2js-preprocessor 包:

      describe("MyCtrl's form", function () {
          var $scope,
              MyCtrl;
      
          beforeEach(angular.mock.module("my.module"));
      
          beforeEach(inject(function (_$rootScope_, _$controller_, _$compile_) {
              $scope = _$rootScope_.$new();
      
              // Execute the controller's logic
              // Omit the ' as vm' suffix if you are not using controllerAs
              MyCtrl = _$controller_("MyCtrl as vm", { $scope: $scope });
      
              // Compile the template against our scope to populate form variables
              var html = require("./my.template.html"),
                  template = angular.element(html);
      
              _$compile_(template)($scope);
          }));
      
          it('should be invalid when given bad input', function () {
              MyCtrl.form.number.$setViewValue('Not a number');
              expect(MyCtrl.form.number.$valid).toBeFalsy();
              expect(MyCtrl.form.$valid).toBeFalsy();
          });
      });
      

      HTML:

      <form name="vm.form">
          <input type="text" name="number" ng-pattern="/^d+$/">
      </form>
      

      【讨论】:

        猜你喜欢
        • 2016-08-24
        • 2014-04-06
        • 1970-01-01
        • 1970-01-01
        • 2012-01-08
        • 2019-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多