javascript 如何在 Jasmine 中编写 FileReader 测试?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15212348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 23:53:04  来源:igfitidea点击:

How do I write FileReader test in Jasmine?

javascripthtmlunit-testingjasmine

提问by toy

I'm trying to make this test work, but I couldn't get my head around how to write a test with FileReader. This is my code

我正在尝试使这个测试工作,但我无法理解如何使用 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);
            }
        }
    };
})();



And this is my test

这是我的测试


describe("Uploader", function() {
    it("should upload a file successfully", function() {
        spyOn($, "ajax");
        var fakeFile = {};

        var uploader = new Uploader(fakeFile);
        uploader.upload();

        expect($.ajax.mostRecentCall.args[0]["url"]).toEqual("/upload/file");
    })
});

But it never gets to reader.onload.

但它永远不会达到reader.onload

采纳答案by Andreas K?berle

The problem here is the use of reader.onloadwhich is hard to test. You could use reader.addEventListenerinstead so you can spy on the global FileReader object and return a mock:

这里的问题reader.onload是难以测试的使用。您可以reader.addEventListener改为使用,以便您可以监视全局 FileReader 对象并返回一个模拟:

eventListener = jasmine.createSpy();
spyOn(window, "FileReader").andReturn({
 addEventListener: eventListener
})

then you can fire the onload callback by yourself:

然后你可以自己触发 onload 回调:

expect(eventListener.mostRecentCall.args[0]).toEqual('load');
eventListener.mostRecentCall.args[1]({
  target:{
    result:'the result you wanna test'
  }
})

回答by Tom Elmore

This syntax changed in 2.0. Code below gives an example based on Andreas K?berle's answer but using the new syntax

此语法在 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);

回答by NVCoder

I also faced similar problem and was able to achieve it without use of addeventlistener. I had used onloadend, so below is what I did. My ts file had below code:-

我也遇到了类似的问题,并且能够在不使用 addeventlistener 的情况下实现它。我使用过onloadend,所以下面是我所做的。我的 ts 文件有以下代码:-

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

My spec file (test) case code :-

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

 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();
    });

This way you have mocked the FileReader object and returned a fake call to your own "readAsDataURL". And thus now when your actual code calls "reasAsDataURL" your fake function is called in which you are assigning an encoded string in "result" and calling "onloadend" function which you had already assigned a functionality in your code (.ts) file. And hence it gets called with expected result. Hope it helps.

通过这种方式,您模拟了 FileReader 对象并返回了对您自己的“readAsDataURL”的虚假调用。因此,现在当您的实际代码调用“reasAsDataURL”时,您的假函数将被调用,您在“result”中分配一个编码字符串并调用您已经在代码 (.ts) 文件中分配了一个功能的“onloadend”函数。因此它以预期的结果被调用。希望能帮助到你。

回答by Lev Savranskiy

i found easiest for myself to do next.

我发现自己接下来最容易做。

  • mock blob file
  • run reader.onloadwhile in test environment.
  • 模拟 blob 文件
  • 在测试环境中运行 reader.onload

as result - i do not mock Filereader

结果 - 我不嘲笑 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("");

});

回答by Warren Barnes

I struggled to figure out how to test onloadendwhen it gets called from readAsDataURL. Here is a dump of what I ended up with.

我很难弄清楚如何测试onloadend何时从readAsDataURL. 这是我最终得到的转储。

Production code:

生产代码:

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 = "";
    }

Test code:

测试代码:

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();
            });
        });