我该如何在Jasmine中编写FileReader测试?

24

我正在尝试使这个测试工作,但我无法理解如何使用FileReader编写测试。这是我的代码


function Uploader(file) {
    this.file = file;
}
Uploader.prototype = (function() {
function upload_file(file, file_contents) { var file_data = new FormData() file_data.append('filename', file.name) file_data.append('mimetype', file.type) file_data.append('data', file_contents) file_data.append('size', file.size)
$.ajax({ url: "/upload/file", type: "POST", data: file_contents, contentType: file.type, success: function(){
// $("#thumbnail").attr("src", "/upload/thumbnail");
}, error: function(){ alert("Failed"); }, xhr: function() { myXhr = $.ajaxSettings.xhr(); if(myXhr.upload){ myXhr.upload.addEventListener('progress',showProgress, false); } else { console.log("Upload progress is not supported."); } return myXhr; } }); }
return { upload : function() { var self = this, reader = new FileReader(), file_content = {};
reader.onload = function(e) { file_content = e.target.result.split(',')[1];
upload_file(self.file, file_content); } } }; })();

这是我的测试


描述("上传器", function() {
    它("应该成功上传文件", function() {
        spyOn($, "ajax");
        var fakeFile = {};
var uploader = new Uploader(fakeFile); uploader.upload();
expect($.ajax.mostRecentCall.args[0]["url"]).toEqual("/upload/file"); }) });

但它从未到达reader.onload

6个回答

17
这里的问题在于使用了难以测试的reader.onload。你可以使用reader.addEventListener代替,这样就可以监视全局的FileReader对象并返回一个模拟对象:
eventListener = jasmine.createSpy();
spyOn(window, "FileReader").andReturn({
 addEventListener: eventListener
})

然后您可以自己触发 onload 回调函数:
expect(eventListener.mostRecentCall.args[0]).toEqual('load');
eventListener.mostRecentCall.args[1]({
  target:{
    result:'the result you wanna test'
  }
})

我卡在这里了。有人能提供一个可行的例子吗?我一直得到未定义的结果(使用Karma / Angular / Jasmine)。 - Jan Petzold
4
这个已经过时了。 - allenhwkim
现在我认为最好根本不要模拟FileReader,请参见下面的答案。 - AJ Richardson

15

这个语法在2.0中有所改变。下面的代码提供了一个基于Andreas Köberle的答案的示例,但使用了新语法。

    // create a mock object, its a function with some inspection methods attached
    var eventListener = jasmine.createSpy();

    // this is going to be returned when FileReader is instantiated
    var dummyFileReader = { addEventListener: eventListener };

    // pipe the dummy FileReader to the application when FileReader is called on window
    // this works because window.FileReader() is equivalent to new FileReader()
    spyOn(window, "FileReader").and.returnValue(dummyFileReader)

    // your application will do something like this ..
    var reader = new FileReader();

    // .. and attach the onload event handler
    reader.addEventListener('load', function(e) {

        // obviously this wouldnt be in your app - but it demonstrates that this is the 
        // function called by the last line - onloadHandler(event);
        expect(e.target.result).toEqual('url');

        // jasmine async callback
        done();
    });

    // if addEventListener was called on the spy then mostRecent() will be an object. 
    // if not it will be null so careful with that. the args array contains the 
    // arguments that addEventListener was called with. in our case arg[0] is the event name ..
    expect(eventListener.calls.mostRecent().args[0]).toEqual('load');

    // .. and arg[1] is the event handler function
    var onloadHandler = eventListener.calls.mostRecent().args[1];

    // which means we can make a dummy event object .. 
    var event = { target : { result : 'url' } };

    // .. and call the applications event handler with our test data as if the user had 
    // chosen a file via the picker
    onloadHandler(event);

我发现一个奇怪的问题,如果你在全局范围内隐式地创建FileReader,例如 new FileReader(),jasmine无法找到它,但是如果我调用 new window.FileReader(),那么spy就会按预期起作用。这让我花了将近一个小时才弄清楚。 - Justus Romijn

12

我也遇到了类似的问题,并且成功地在不使用addeventlistener的情况下实现了它。我使用了onloadend,以下是我的做法。

我的TS文件包含以下代码:

    let reader = new FileReader();
    reader.onloadend = function() {
                let dataUrl = reader.result;
                // Some working here
            };
    reader.readAsDataURL(blob);

我的规范文件(测试)案例代码:

 let mockFileReader = {
            result:'',
            readAsDataURL:(blobInput)=> {
                console.log('readAsDataURL');
            },
            onloadend:()=> {
                console.log('onloadend');
            }
        };

    spyOn<any>(window, 'FileReader').and.returnValue(mockFileReader);
    spyOn<any>(mockFileReader, 'readAsDataURL').and.callFake((blobInput)=> {
        // debug your running application and assign to "encodedString" whatever 
        //value comes actually after using readAsDataURL for e.g. 
        //"data:*/*;base64,XoteIKsldk......"
        mockFileReader.result = encodedString;
        mockFileReader.onloadend();
    });

这样你就模拟了FileReader对象并返回了一个对自己的readAsDataURL的伪调用。因此,当你的实际代码调用reasAsDataURL时,会调用你的伪函数,在其中将编码字符串分配给result并调用已经在你的代码(.ts)文件中分配功能的onloadend函数。因此,它会按预期结果被调用。

4

我认为最好的方法是使用真正的FileReader(不要模拟它),并传入真正的FileBlob。这可以提高测试覆盖率,使测试更加健壮。

如果你的测试在IE中无法运行,你可以使用File构造函数,例如:

const fakeFile = new File(["some contents"], "file.txt", {type: "text/plain"});

如果需要与IE兼容,可以构建一个 Blob 并将其看作文件:Blob
const fakeFile = new Blob(["some contents"]);
fakeFile.name = "file.txt";
fakeFile.type = "text/plain";
< p> FileReader 可以读取这两个对象,因此无需模拟它。 < / p>

今天我在解决这个问题时遇到了困难,你提供的解决方案确实让它变得更简单了。谢谢! - MikeC
1
如果您想测试“onerror”路径,该怎么做呢? - PMO1948
我不确定是否可能 - 你可能需要使用其他答案中的模拟方法。 - AJ Richardson

1
我发现最容易做的事情是:
  • 模拟 Blob 文件
  • 在测试环境中运行 reader.onload
结果是 - 我不需要模拟 Filereader。

//    CONTROLLER

$scope.handleFile = function (e) {

            var f = e[0];

            $scope.myFile = {
                name: "",
                size: "",
                base64: ""
            };
            var reader = new FileReader();
            reader.onload = function (e) {
                        try {
                            var buffer = e.target.result;
                            $scope.myFile = {
                                name: f.name,
                                size: f.size,
                                base64: XLSX.arrayBufferToBase64(buffer)
                            };
                            $scope.$apply();

                        } catch (error) {
                            $scope.error = "ERROR!";
                            $scope.$apply();
                        }
                    };

reader.readAsArrayBuffer(f);
//run in test env
if ( typeof jasmine == 'object') {reader.onload(e)}
}

//JASMINE TEST

it('handleFile    0', function () {


    var  fileContentsEncodedInHex = ["\x45\x6e\x63\x6f\x64\x65\x49\x6e\x48\x65\x78\x42\x65\x63\x61\x75\x73\x65\x42\x69\x6e\x61\x72\x79\x46\x69\x6c\x65\x73\x43\x6f\x6e\x74\x61\x69\x6e\x55\x6e\x70\x72\x69\x6e\x74\x61\x62\x6c\x65\x43\x68\x61\x72\x61\x63\x74\x65\x72\x73"];
    var blob = new Blob(fileContentsEncodedInHex);
    blob.type = 'application/zip';
    blob.name = 'name';
    blob.size = 11111;
    var e = {0: blob, target: {result: {}}};

    $scope.handleFile(e);
    expect($scope.error ).toEqual("");

});


修改代码以使测试通过并不是一个好主意。 - LocustHorde

0

我曾经苦苦思索如何测试从readAsDataURL调用时的onloadend。 以下是我最终得出的结果。

生产代码:

loadFileDataIntoChargeback(tempFileList) {
        var fileNamesAndData = [];
        for (var i = 0, f; f = tempFileList[i]; i++) {
            let theFile = tempFileList[i];
            var reader = new FileReader();
            reader.onloadend = ((theFile) => {
                return (fileData) => {
                    var insertionIndex = this.chargeback.fileList.length;
                    this.chargeback.fileList.push({ FileName: theFile.name, Data: fileData.target.result, FileType: theFile.type });
                    this.loadFilePreviews(theFile, insertionIndex);
                }
            })(f);
            reader.readAsDataURL(f);
        }
        this.fileInputPath = "";
    }

测试代码:

describe('when the files are loaded into the chargeback', () => {
            it('loads file previews', () => {
                let mockFileReader = {
                    target: { result: '' },
                    readAsDataURL: (blobInput) => {},
                    onloadend: () => {}
                };
                spyOn(chargeback, "loadFilePreviews");
                spyOn(window, 'FileReader').and.returnValue(mockFileReader);
                spyOn(mockFileReader, 'readAsDataURL').and.callFake((blobInput) => {
                    mockFileReader.onloadend({ target: { result: "data:image/jpeg;base64,/9j/4QAYRXh" } });
                });
                var readFileList = chargeback.getArrayFromFileInput([getImageFile1()]);

                chargeback.loadFileDataIntoChargeback(readFileList);

                expect(chargeback.loadFilePreviews).toHaveBeenCalled();
            });
        });

@superITguy,恐怕我不记得了。抱歉! - Warren Barnes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接