如何提供模拟文件来更改 <input type='file'> 的事件用于单

How to provide mock files to change event of lt;input type=#39;file#39;gt; for unit testing(如何提供模拟文件来更改 lt;input type=filegt; 的事件用于单元测试)
本文介绍了如何提供模拟文件来更改 <input type='file'> 的事件用于单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

我在进行单元测试时遇到了困难,我想在其中验证文件的处理,通常可以通过 <input type='file'> 在视图中选择该文件.

I'm having difficulties with a unit test in which I want to verify the processing of a file, which would usually be selected in the view via <input type='file'>.

在我的 AngularJS 应用程序的控制器部分,文件在输入的更改事件中处理,如下所示:

In the controller part of my AngularJS app the file is processed inside the input's change event like so:

//bind the change event of the file input and process the selected file
inputElement.on("change", function (evt) {
    var fileList = evt.target.files;
    var selectedFile = fileList[0];
    if (selectedFile.size > 500000) {
        alert('File too big!');
    // ...

我希望 evt.target.files 在我的单元测试中包含我的模拟数据,而不是用户选择的文件.我意识到我自己无法实例化 FileListFile 对象,这将是浏览器正在使用的相应对象.因此,我将模拟 FileList 分配给输入的 files 属性并手动触发更改事件:

I'd like evt.target.files to contain my mock data instead of the user's selected file in my unit test. I realized that I can't instantiate a FileList and File object by myself, which would be the according objects the browser is working with. So I went with assigning a mock FileList to the input's files property and triggering the change event manually:

describe('document upload:', function () {
    var input;

    beforeEach(function () {
        input = angular.element("<input type='file' id='file' accept='image/*'>");
        spyOn(document, 'getElementById').andReturn(input);
        createController();
    });

    it('should check file size of the selected file', function () {
        var file = {
            name: "test.png",
            size: 500001,
            type: "image/png"
        };

        var fileList = {
            0: file,
            length: 1,
            item: function (index) { return file; }
        };

        input.files = fileList; // assign the mock files to the input element 
        input.triggerHandler("change"); // trigger the change event

        expect(window.alert).toHaveBeenCalledWith('File too big!');
    });

不幸的是,这会导致控制器中出现以下错误,表明此尝试失败,因为文件根本没有分配给输入元素:

Unfortunately, this causes the following error in the controller which shows that this attempt failed because the files were not assigned to the input element at all:

TypeError: 'undefined' 不是对象(评估 'evt.target.files')

出于安全原因,我已经发现 input.files 属性是只读的.所以我开始了另一种方法,通过调度一个定制的更改来提供 files 属性,但仍然没有成功.

I already found out that the input.files property is read-only for security reasons. So I started another approach by dispatching a customized change which would provide the files property, but still without success.

长话短说:我很想学习一个可行的解决方案或任何关于如何处理这个测试用例的最佳实践.

推荐答案

让我们重新思考 AngularJS,DOM 必须在指令中处理

Let's rethink AngularJS, DOM must be handled in a directive

我们不应该在控制器中处理 DOM 元素,即 element.on('change', ..,尤其是为了测试目的.在控制器中,您与数据交谈,而不是与 DOM.

We should not deal with DOM element in a controller, i.e. element.on('change', .., especially for testing purpose. In a controller, You talk to data, not to DOM.

因此,那些 onchange 应该是如下的指令

Thus, those onchange should be a directive like the following

<input type="file" name='file' ng-change="fileChanged()" /> <br/>

但是,不幸的是,ng-change 不适用于 type="file".我不确定未来的版本是否适用于此.我们仍然可以应用相同的方法.

However, unfortunately, ng-change does not work well with type="file". I am not sure that the future version works with this or not. We still can apply the same method though.

<input type="file" 
  onchange="angular.element(this).scope().fileChanged(this.files)" />

在控制器中,我们只是简单地定义一个方法

and in the controller, we just simply define a method

$scope.fileChanged = function(files) {
  return files.0.length < 500000;
};

现在,一切都只是普通的控制器测试.不再需要处理 angular.element$compiletriggers 等!:)

Now, everything is just a normal controller test. No more dealing with angular.element, $compile, triggers, etc.! :)

describe(‘MyCtrl’, function() {
  it('does check files', inject(
    function($rootScope, $controller) {
      scope = $rootScope.new();
      ctrl = $controller(‘UploadCtrl’, {‘$scope’: scope});

      var files = { 0: {name:'foo', size: 500001} };
      expect(scope.fileChanged(files)).toBe(true);
    }
  ));
});

http://plnkr.co/edit/1J7ETus0etBLO18FQDhK?p=preview

这篇关于如何提供模拟文件来更改 &lt;input type='file'&gt; 的事件用于单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

本站部分内容来源互联网,如果有图片或者内容侵犯了您的权益,请联系我们,我们会在确认后第一时间进行删除!

相关文档推荐

jasmine.createSpyObj with properties(jasmine.createSpyObj 与属性)
What#39;s a good way to reuse test code using Jasmine?(使用 Jasmine 重用测试代码的好方法是什么?)
Does Jasmine#39;s toThrow matcher require the argument to be wrapped in an anonymous function?(Jasmine 的 toThrow 匹配器是否需要将参数包装在匿名函数中?)
How to mock window.location.href with Jest + Vuejs?(如何用 Jest + Vuejs 模拟 window.location.href?)
Jasmine - Spying on a method call within a constructor(Jasmine - 在构造函数中监视方法调用)
Getting requirejs to work with Jasmine(让 requirejs 与 Jasmine 一起工作)