简体   繁体   English

如何在 Jasmine 中编写 FileReader 测试?

[英]How do I write FileReader test in Jasmine?

I'm trying to make this test work, but I couldn't get my head around how to write a test with FileReader.我正在尝试使这个测试工作,但我无法理解如何使用 FileReader 编写测试。 This is my code这是我的代码


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

The problem here is the use of reader.onload which is hard to test.这里的问题是使用了难以测试的reader.onload You could use reader.addEventListener instead so you can spy on the global FileReader object and return a mock:您可以使用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'
  }
})

This syntax changed in 2.0.此语法在 2.0 中发生了变化。 Code below gives an example based on Andreas Köberle's answer but using the new syntax下面的代码给出了一个基于 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);

I also faced similar problem and was able to achieve it without use of addeventlistener.我也遇到了类似的问题,并且能够在不使用 addeventlistener 的情况下实现它。 I had used onloadend, so below is what I did.我使用过onloadend,所以下面是我所做的。 My ts file had below code:-我的 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".通过这种方式,您模拟了 FileReader 对象并返回了对您自己的“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.因此,现在当您的实际代码调用“reasAsDataURL”时,您的假函数被调用,在该函数中您在“result”中分配一个编码字符串并调用您已经在代码(.ts)文件中分配了一个功能的“onloadend”函数。 And hence it gets called with expected result.因此它以预期的结果被调用。 Hope it helps.希望能帮助到你。

I think the best way is to use the real FileReader (don't mock it), and pass in a real File or Blob .我认为最好的方法是使用真正的FileReader (不要嘲笑它),并传入一个真正的FileBlob This improves your test coverage and makes your tests less brittle.这提高了您的测试覆盖率并使您的测试不那么脆弱。

If your tests don't run in IE, you can use the File constructor , eg如果您的测试不在 IE 中运行,您可以使用File 构造函数,例如

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

If you need to be compatible with IE, you can construct a Blob and make it look like a file:如果需要兼容IE,可以构造一个Blob ,让它看起来像一个文件:

const fakeFile = new Blob(["some contents"]);
fakeFile.name = "file.txt";
fakeFile.type = "text/plain";

The FileReader can read either of these objects so there is no need to mock it. FileReader可以读取这些对象中的任何一个,因此无需模拟它。

i found easiest for myself to do next.我发现自己接下来最容易做。

  • mock blob file模拟 blob 文件
  • run reader.onload while in test environment.在测试环境中运行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(""); });

I struggled to figure out how to test onloadend when it gets called from readAsDataURL .我挣扎着要弄清楚如何测试onloadend时,它会从被称为readAsDataURL Here is a dump of what I ended up with.这是我最终得到的转储。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM