用于单元测试的模拟文件输入

时间:2016-03-31 10:20:52

标签: javascript jasmine

我得到了类似这些代码的内容:

jQuery('.js-img-input').on('change', handleNewFiles);   

var handleNewFiles = function(event) {
    var fileList = event.target.files;
    loadFileList(fileList);
};

var loadFileList = function(fileList) {
    jQuery(fileList).each(function(key, file) {
        readFileAsync(file);
    });
}

var readFileAsync = function(file) {
    var fileReader = new FileReader();
    fileReader.addEventListener("load", function(event) {
        file.result = event.target.result;
        saveFile(file);
    });
    fileReader.readAsDataURL(file);
};

所有方法都是jQuery函数内部的私有方法,我不打算将“handleNewFiles”公开用于测试目的。
我想用这样的东西测试这些线:

it('should create a fileReader', function(){
    spyOn(window, 'FileReader').and.returnValue({
        addEventListener: function(){},
        readAsDataURL: function(){}
    });
    jQuery('.js-img-input').trigger('change');
    expect(window.FileReader).toHaveBeenCalled();
});

但是我如何将一些虚拟数据导入 event.target.files

4 个答案:

答案 0 :(得分:3)

好吧我找到了解决方案。您无法使用文件创建伪造的本机事件,出于安全考虑,这是一件好事。但就我而言,解决方案看起来像这样:

it('should create a fileReader', function(){
  var onChangeCallback;
  spyOn(jQuery.fn, 'on').and.callFake(function(eventName, callback){
    if(eventName === 'change'){
      onChangeCallback = callback;
    }
  });
  spyOn(window, 'FileReader').and.returnValue({
    addEventListener: function(){},
    readAsDataURL: function(){}
  });
  onChangeCallback({target:{files:[1,2,3]}});
  expect(window.FileReader).toHaveBeenCalled();
});

答案 1 :(得分:1)

这篇文章可能有帮助

http://nerds.intuo.io/2016/05/12/mocking-file-uploads-in-javascript.html



function fillInFileInput(selector, file) {
  // Get the input
  let input = jQuery(selector);

  // Get out file options
  let { name, type, content } = file;

  // Create a custom event for change and inject target
  let event = jQuery.Event('change', {
    target: {
      files: [{
        name: name, type: type
      }]
    }
  });

  // Stub readAsDataURL function
  let stub = sinon.stub(FileReader.prototype, 'readAsDataURL', function() {
    this.onload({ target: { result: content }});
  });

  // Trigger event
  input.trigger(event);

  // We don't want FileReader to be stubbed for all eternity
  stub.restore();
}




答案 2 :(得分:0)

我发布了一个基于Jakob答案的略微修改过的解决方案。我需要一些细微的修改才能让它适合我。这对我使用“angular”:“1.0.6”,“jasmine-node”:“1.3.1”,“jquery”:“1.10.0”

var onChangeCallback;

spyOn(jQuery.fn, 'on').andCallFake(function(eventName, selector, callback){
  if(eventName === 'change'){
    onChangeCallback = callback;
  }
});
spyOn(window, 'FileReader').andReturn({
  addEventListener: function(){},
  readAsDataURL: function(){}
});

onChangeCallback({currentTarget: {files:[{size: 4000}], value: 'asd'}});

答案 3 :(得分:0)

虽然您无法为fileInput.files分配值,但是您可以用自己的属性替换该属性,从而实现相同的目的:

it('should create a fileReader', () => {
    const fileInput = $('input[type=file]');
    const fileInputElement = fileInput.get(0);
    Object.defineProperty(fileInputElement, 'files', {
      value: [{name: 'file.txt'}],
      writable: false,
    });
    fileInput.trigger('input').trigger('change');

    // expect outputs...
});

这比创建自己的事件对象要好一些,因为它可以更精确地模拟真实环境。