如何提供模拟文件,更改&LT的事件;输入类型='文件'>单元测试文件、单元测试、类型、事件

2023-09-13 02:55:58 作者:薄涼之人怎能溫暖他人i

我在其中我想验证一个文件,这通常会在视图中通过选择的处理与单元测试的困难<输入类型='文件'>

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 包含在我的单元测试用户选择的文件,而不是我的模拟数据。我意识到,我不能用自己实例化一个文件清单文件对象,这将是根据对象的浏览器正在与。所以我分配一个模拟文件清单到输入的文件财产和手动触发事件改变了:

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:

类型错误:未定义是不是(评估'evt.target.files')

我已经发现,在 input.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('变',.. ,特别是用于测试目的。在一个控制器,你说话数据,而不是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-变化不很好地工作类型=文件。我不知道未来的版本适用于本与否。我们仍然可以申请,虽然同样的方法。

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 $处理编译触发等! :)

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=$p$ PVIEW