2013-01-07 47 views
4

在我的應用程序中,用戶使用HTML5拖放處理二進制文件。代碼的那部分工作正常。在chrome中,我拖動一個二進制文件並使用FileReader創建一個arrayBuffer。這一切似乎都很好。我正在爲此功能編寫測試,而且我很茫然。如何將二進制文件加載到我的單元測試中?對於我正在測試的代碼位,我只需要一個arrayBuffer。目前,我正手動創建arrayBuffer,但這不是一個可持續的解決方案。爲了使我的測試有效,我需要能夠隨時拋出一個新的二進制文件並進行新的測試。我的測試環境是唾手可得+茉莉花。如何在javascript單元測試期間加載二進制文件?

(function() {"use strict"; 
    function loadSimpleDataView() { 
     //instead of defining ArrayBuffer, 
     //I want it to be generated based upon an external file 
     var buffer = new ArrayBuffer(4), dataView = new DataView(buffer), int8View = new Int8Array(buffer); 
     int8View.set([0x00,0x01,0x02,0x03]); 

     return dataView; 
    } 

    describe('mymodule', function() { 

     it('mytest', function() { 
     var dataView = loadSimpleDataView(); 

     expect(dataView).toBeDefined(); 
     //do rest of tests 
     }); 
    }); 
    }()); 

回答

2

我使用grunt來構建我的項目並運行我的單元測試。以下是我的grunt.js,testacular.conf.js和測試(javaclassstreamreader.spec.js)。簡而言之,grunt啓動grunt-server,它被配置爲提供二進制數據文件。口足被配置爲代理grunt-server。在測試中,XMLHttpRequest用於檢索二進制文件。一切正常,但似乎有點複雜。有更容易的方法嗎?

grunt.js:

/*global module:false*/ 
module.exports = function(grunt) {"use strict"; 

    // Project configuration. 
    grunt.initConfig({ 
    pkg : '<json:package.json>', 
    meta : { 
     banner : '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + '<%= grunt.template.today("yyyy-mm-dd") %>\n' + '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */' 
    }, 
    lint : { 
     files : ['grunt.js', 'src/*.js', 'src/public/js/**/*.js', 'src/specs/**/*.js'] 
    }, 
    watch : { 
     files : '<config:lint.files>', 
     tasks : 'default' 
    }, 
    exec : { 
     ensure_generated_directory : { 
     command : 'mkdir -p generated/js/' 
     } 
    }, 
    clean : { 
     all : ['generated'] 
    }, 
    jshint : { 
     files : '<config:lint.files>', 
     options : { 
     curly : true, 
     eqeqeq : true, 
     forin : true, 
     immed : true, 
     latedef : true, 
     newcap : true, 
     noarg : true, 
     sub : true, 
     undef : true, 
     unused : true, 
     strict : true, 
     boss : true, 
     eqnull : true, 
     es5 : true, 
     browser : true, 
     jquery : true, 
     devel : true 
     }, 
     globals : { 
     //jasmine 
     describe : false, 
     it : false, 
     expect : false, 
     //commonjs 
     require : false, 
     exports : true, 
     //angular 
     angular : false 
     } 
    }, 
    'closure-compiler' : { 
     frontend : { 
     closurePath : 'closure-compiler', 
     js : ['src/*.js', 'src/public/js/**/*.js'], 
     jsOutputFile : 'generated/js/complete-app.js', 
     options : { 
      externs : 'externs.js', 
      compilation_level : 'SIMPLE_OPTIMIZATIONS', 
      language_in : 'ECMASCRIPT5_STRICT', 
      logging_level : 'ALL', 
      debug : null, 
      warning_level : 'verbose', 
      summary_detail_level : 3, 
      formatting : ['PRETTY_PRINT', 'PRINT_INPUT_DELIMITER'], 
      common_js_entry_module : 'src/public/js/app.js', 
      process_common_js_modules : null, 
      process_jquery_primitives : null, 
      common_js_module_path_prefix : 'src' 
     } 
     } 
    }, 
    testacularServer : { 
     integration : { 
     options : { 
      keepalive : true 
     }, 
     configFile : 'testacular.conf.js', 
     autoWatch : false, 
     singleRun : true 
     } 
    }, 
    server : { 
     port : 18081, 
     base : './src/specs/data' 
    } 
    }); 

    // Default task. 
    grunt.registerTask('default', 'lint exec:ensure_generated_directory closure-compiler server testacularServer:integration'); 
    grunt.loadNpmTasks('grunt-contrib-watch'); 
    grunt.loadNpmTasks('grunt-closure-compiler'); 
    grunt.loadNpmTasks('grunt-exec'); 
    grunt.loadNpmTasks('grunt-contrib-clean'); 
    grunt.loadNpmTasks('grunt-testacular'); 
}; 

testacular.conf.js:

// Testacular configuration 
// Generated on Tue Jan 01 2013 03:17:01 GMT-0500 (EST) 
/*global basePath:true */ 
/*global files:true */ 
/*global JASMINE:false */ 
/*global JASMINE_ADAPTER:false */ 
/*global exclude:true */ 
/*global reporters:true */ 
/*global port:true */ 
/*global runnerPort:true */ 
/*global colors:true */ 
/*global logLevel:true */ 
/*global LOG_INFO:false */ 
/*global autoWatch:true */ 
/*global browsers:true */ 
/*global captureTimeout:true */ 
/*global singleRun:true */ 

// base path, that will be used to resolve files and exclude 
basePath = '.'; 


// list of files/patterns to load in the browser 
files = [ 
    JASMINE, 
    JASMINE_ADAPTER, 
    'src/public/lib/jquery/1.7.2/jquery-1.7.2.min.js', 
    'src/public/lib/jquery-ui/1.8.20.custom/jquery-ui-1.8.20.custom.min.js', 
    'src/public/lib/angular/1.0.1/angular-1.0.1.min.js', 
    'src/public/lib/filer.min.js', 
    'generated/js/complete-app.js', 
    'src/specs/**/*.spec.js' 
]; 


// list of files to exclude 
exclude = [ 
]; 


// test results reporter to use 
// possible values: 'dots', 'progress', 'junit' 
reporters = ['progress']; 


// web server port 
port = 18080; 


// cli runner port 
runnerPort = 9100; 

//proxy 
proxies = { 
    '/test-data/': 'http://localhost:18081/' 
}; 

// enable/disable colors in the output (reporters and logs) 
colors = true; 


// level of logging 
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 
logLevel = LOG_INFO; 


// enable/disable watching file and executing tests whenever any file changes 
autoWatch = true; 


// Start these browsers, currently available: 
// - Chrome 
// - ChromeCanary 
// - Firefox 
// - Opera 
// - Safari (only Mac) 
// - PhantomJS 
// - IE (only Windows) 
browsers = ['Chrome']; 


// If browser does not capture in given timeout [ms], kill it 
captureTimeout = 5000; 


// Continuous Integration mode 
// if true, it capture browsers, run tests and exit 
singleRun = false; 

javaclassstreamreader.spec.js:

/*global module$javaclassstreamreader:false */ 
/*global waitsFor:false */ 
/*global runs:false */ 
/*global dump:false */ 


(function() {"use strict"; 

    var JavaClassStreamReader = module$javaclassstreamreader.JavaClassStreamReader; 

    function loadSimpleDataView(callback) { 
     var dataView; 
     var xhr = new XMLHttpRequest(); 
     xhr.open('GET', '/test-data/MyInterface.class', true); 
     xhr.responseType = 'arraybuffer'; 
     xhr.onload = function() { 
      dataView = new DataView(this.response); 
      callback(dataView); 
     }; 
     xhr.onerror = dump; 
     xhr.send(); 
    } 

    describe('javaclassstreamreader', function() { 

     it('reader can be constructed', function() { 
     var hasData = false,reader; 

     loadSimpleDataView(function(dataView) { 
      reader = new JavaClassStreamReader(dataView); 
      hasData = true; 
     }); 

     waitsFor(function() { 
      return hasData; 
     }, "Never retrieved file", 3000); 
     runs(function() { 

      expect(reader.offset).toBe(0); 

      var firstBytes = reader.getU4(); 
      dump(firstBytes.toString(16)); 
      expect(firstBytes).toBe(0xcafebabe); 
      expect(reader.maxOffset).toBe(126); 
     }); 
     }); 

    }); 

    }()); 
5

我通過編碼的二進制文件解決了這個問題作爲十六進制字符串,將其填充到blob中,然後調用接受File對象的函數。我使用Jasmine來測試文件加載函數。 FileLoader是我寫的具有loadFromFile函數的類。

beforeEach(function() { 
    return this.fileLoader = new FileLoader() }); 

    it("can load this bin file safely", function() { 
    var blob, fileContentsEncodedInHex; 

    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"]; 

    blob = new Blob(fileContentsEncodedInHex); 

    this.fileLoader.loadFromFile(blob); 
    }); 

在文件加載類的功能看起來是這樣的(在CoffeeScript中)。你應該錯誤處理絕對添加到您的商業代碼...

# 
    # Load the file 
    # 
    # @param [File] file 
    # The DOM file object 
    # 
    loadFromFile: (file) -> 
    # create the file reader 
    # assign the file load handler 
    # read the array as a buffer, which will trigger the handler 
    reader = new FileReader() 
    reader.onloadend = this._handleOnLoadEnd 
    reader.readAsArrayBuffer(file) 
    return 

我寫了下面的小Python應用程序輸出文件內容可以作爲一個javascript內容的十六進制編碼字符串串。 (僅供參考,這是我12年來的第一個Python腳本,我確定它效率不高,但速度很快)非常感謝同事輸出字符串。我不確定爲什麼代碼塊在顯示屏上變得亂七八糟。我很抱歉。

import sys 
fulldoc = "" 
with open('your file', 'rb') as readFile: 
    while 1: character = readFile.read(1) 
     if not character: 
      break 
     fulldoc = fulldoc + "\\x" + character.encode("hex") 
print fulldoc 

附加花絮......我不知道您的具體情況,但如果您需要在任何時間,並重新添加一個新的二進制文件,我建議以下...

- 運行測試,然後你應該對每種隨機添加的二進制文件進行測試。測試正面和負面文件(即,應該與您的應用程序一起使用的文件,不應該與您的應用程序一起使用的文件)。這就是單元測試框架的用處。將來,如果您發現由於代碼中的錯誤而導致應用程序崩潰的文件,則可以修復該錯誤,添加新的單元測試以加載該二進制文件,然後測試應該通過。如果你碰巧在某些時候打破了意外的文件處理代碼,單元測試總是在那裏向你顯示出錯。

+0

我第一次啓動時做了類似的事情,但是我不想每次想測試時都要轉換文件。但是,設置比網絡服務器更簡單,所以我很感謝你添加一個替代方案。 – pgreen2

+0

您的解決方案對於小型二進制數據集似乎確實很好,但我需要加載更大的文件。 – pgreen2

2

我想你可以繞過在另一個端口上運行一個額外的grunt服務器來提供二進制文件。在最新版本的業力中,您可以定義一些有關文件如何包含和由業務服務器提供服務的細節。所以,你會在文件中包含您的測試數據,並告訴噶服務,但不能觀看或包括

files = [ 
    JASMINE, 
    JASMINE_ADAPTER, 
    'src/public/lib/jquery/1.7.2/jquery-1.7.2.min.js', 
    'src/public/lib/jquery-ui/1.8.20.custom/jquery-ui-1.8.20.custom.min.js', 
    'src/public/lib/angular/1.0.1/angular-1.0.1.min.js', 
    'src/public/lib/filer.min.js', 
    /* NEW LINE NEEDED BELOW! */ 
    {pattern: 'src/specs/data/**', watched: false, included: false, served: true}, 
    'generated/js/complete-app.js', 
    'src/specs/**/*.spec.js' 
]; 

然後在您的測試,你需要獲得該文件的適當位置插入XHR文件。打開方法。如果你將一個空字符串傳入xhr.open('GET','')並轉儲responseText,你將得到一個輸出,所有包含的腳本/文件被傳遞給Karma,在那裏,你會找到以'/ base /'開始並且有時候是'/ absolute /'的路徑。不知道100%噶什麼做的,但我嘗試了使用「/基/」爲前綴的xhr.open路徑,它似乎工作:)

var url = '/base/src/specs/data/MyInterface.class'; 
xhr.open('GET', url, true); 

我也是工作的一個項目包括讀取客戶端中的二進制數據文件,並與Karma合作進行測試,因此,閱讀您的問題以獲取靈感很棒。我最終發現了Karma中的新功能對簡化方法非常有用!

相關問題