【问题标题】:How do I write FileReader test in Jasmine?如何在 Jasmine 中编写 FileReader 测试?
【发布时间】:2013-03-04 22:27:23
【问题描述】:

我正在尝试进行此测试,但我不知道如何使用 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); } } }; })();

这是我的测试

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

但它永远不会到达reader.onload

【问题讨论】:

    标签: javascript html unit-testing jasmine


    【解决方案1】:

    这里的问题是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)。
    • 这真是过时了。
    • 现在我认为最好不要模拟 FileReader,请参阅下面的答案。
    【解决方案2】:

    此语法在 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(),那么间谍就会介入正如预期的那样。我花了将近一个小时才弄清楚这一点。
    【解决方案3】:

    我也遇到了类似的问题,并且能够在不使用 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”时,您的假函数被调用,您在“结果”中分配编码字符串并调用您已经在代码(.ts)文件中分配功能的“onloadend”函数。因此它以预期的结果被调用。 希望对您有所帮助。

    【讨论】:

      【解决方案4】:

      我认为最好的方法是使用真实的FileReader(不要嘲笑它),并传入真实的FileBlob。这会提高您的测试覆盖率,并使您的测试不那么脆弱。

      如果您的测试不在 IE 中运行,您可以使用File constructor,例如

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

      如果需要兼容IE,可以构造一个Blob,让它看起来像一个文件:

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

      FileReader 可以读取其中任何一个对象,因此无需模拟它。

      【讨论】:

      • 我今天一直在努力解决这个问题,而您的解决方案确实让它变得简单多了。谢谢!
      • 如果要测试onerror 路径怎么办?你会怎么做?
      • 我不确定这是否可能 - 您可能需要使用其他答案之一的模拟方法。
      【解决方案5】:

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

      • 模拟 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("");
      
      });

      【讨论】:

        【解决方案6】:

        onloadendreadAsDataURL 调用时,我很难弄清楚如何测试它。 这是我最终得到的转储。

        生产代码:

        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 恐怕我不记得了。对不起!
        猜你喜欢
        • 1970-01-01
        • 2021-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-16
        • 2020-11-28
        相关资源
        最近更新 更多