使用mocha javascript测试html5文件api?

sar*_*ast 7 javascript mocha.js filereader html5-filesystem

我在一个网站上有一个简单的图像上传器和一个javascript函数,它使用FileReader并将图像转换为base64,以便用户显示它而无需将其上传到实际的服务器.

function generateThumb(file) {
    var fileReader = new FileReader();
    fileReader.readAsDataURL(file);
    fileReader.onload = function (e) {
        // Converts the image to base64
        file.dataUrl = e.target.result;
    };
}
Run Code Online (Sandbox Code Playgroud)

现在我正在尝试使用Mocha和编写此方法的测试Chai.我的想法是,我想检查是否file.dataUrl已成功创建并且它是base64.所以我想在测试环境中以某种方式模拟本地文件(不知道如何做到这一点).或者我根本不应该测试这个并假设这是有效的?

Esp*_*pen 3

这里的答案取决于一点。除了加载文件内容并将其分配给传入对象的属性之外,您的“generateThumbs”方法是否还有其他逻辑?或者它是否有其他逻辑,例如从图像数据生成缩略图、读取文件属性并将它们分配给文件对象?等等?

如果是这样,那么我实际上建议您用自己的对象模拟 FileReader 对象,以便您可以控制测试结果。但是,您要测试的不是 FileReaders 功能,而是您自己的逻辑。因此,您应该假设 FileReader 可以工作,并测试依赖于它的代码是否可以工作。

现在,由于您发布的方法有点小,在这种情况下,我会假设它有效,重命名该方法并测试其余代码。但是有一个地方可以进行这样的模拟,我必须承认弄清楚如何模拟事件目标非常有趣,所以我将在这里给出一个示例,使用您的方法作为基础:

//This implements the EventTarget interface
//and let's us control when, where and what triggers events
//and what they return
//it takes in a spy and some fake return data

var FakeFileReader = function(spy, fakeData) {
    this.listeners = {};
    this.fakeData = fakeData;
    this.spy = spy;
    this.addEventListener('load', function () {
        this.spy.loaded = true;
    });
};
//Fake version of the method we depend upon
FakeFileReader.prototype.readAsDataURL = function(file){
    this.spy.calledReadAsDataURL = true;
    this.spy.file = file;
    this.result = this.fakeData;
    this.dispatchEvent({type:'load'}); //assume file is loaded, and send event
};
FakeFileReader.prototype.listeners = null;
FakeFileReader.prototype.addEventListener = function(type, callback) {
    if(!(type in this.listeners)) {
        this.listeners[type] = [];
    }
    this.listeners[type].push(callback);
};

FakeFileReader.prototype.removeEventListener = function(type, callback) {
    if(!(type in this.listeners)) {
        return;
    }
    var stack = this.listeners[type];
    for(var i = 0, l = stack.length; i < l; i++) {
        if(stack[i] === callback){
            stack.splice(i, 1);
            return this.removeEventListener(type, callback);
        }
    }
};

FakeFileReader.prototype.dispatchEvent = function(event) {
    if(!(event.type in this.listeners)) {
        return;
    }
    var stack = this.listeners[event.type];
    event.target = this;
    for(var i = 0, l = stack.length; i < l; i++) {
        stack[i].call(this, event);
    }
};

// Your method

function generateThumb(file, reader){
    reader.readAsDataURL(file);
    reader.addEventListener('load', function (e) {
        file.dataUrl = base64(e.target.result);
    });

}
Run Code Online (Sandbox Code Playgroud)

现在我们有一个漂亮的小对象,其行为类似于 FileReader,但我们可以控制它的行为,现在我们可以使用我们的方法并将其注入,从而使我们能够使用此模拟进行测试并使用真实的东西进行生产。

所以我们现在可以编写很好的单元测试来测试这一点:我假设你有 mocha 和 chai 设置

describe('The generateThumb function', function () {
    var file = { src: 'image.file'};
    var readerSpy = {};
    var testData = 'TESTDATA';
    var reader    = new FakeFileReader(readerSpy, testData);
    it('should call the readAsDataURL function when given a file name and a FileReader', function () {
        generateThumb(file, reader);
        expect(readerSpy.calledReadAsDataURL).to.be.true;
        expect(readerSpy.loaded).to.be.true;
    });
    it('should load the file and convert the data to base64', function () {
        var expectedData = 'VEVTVERBVEE=';
        generateThumb(file, reader);
        expect(readerSpy.file.src).to.equal(file.src);
        expect(readerSpy.file.dataUrl).to.equal(expectedData);
    });
});
Run Code Online (Sandbox Code Playgroud)

这是一个工作 JSFiddle 示例: https ://jsfiddle.net/workingClassHacker/jL4xpwwv/2/

这样做的好处是您可以创建此模拟的多个版本:

  • 当给出正确的数据时会发生什么?
  • 当提供不正确的数据时会发生什么?
  • 当回调从未被调用时会发生什么?
  • 当放入太大的文件时会发生什么?
  • 当放入某种哑剧类型时会发生什么?

如果您只需要一个事件,您当然可以大规模简化模拟:

function FakeFileReader(spy, testdata){
    return {
        readAsDataURL:function (file) {
            spy.file = file;
            spy.calledReadAsDataURL = true;
            spy.loaded = true;
            this.target = {result: testdata};
            this.onload(this);
        }
    };
}

function generateThumb(file, reader){
    reader.onload = function (e) {
        file.dataUrl = base64(e.target.result);
    };
    reader.readAsDataURL(file);
}
Run Code Online (Sandbox Code Playgroud)

我实际上就是这样做的。并为不同的目的创建其中几个。

简单版本: https ://jsfiddle.net/workingClassHacker/7g44h9fj/3/