問題描述
我在進行單元測試時遇到了困難,我想在其中驗證文件的處理,通常可以通過 <input type='file'>
在視圖中選擇該文件.
I'm having difficulties with a unit test in which I want to verify the processing of a file, which would usually be selected in the view via <input type='file'>
.
在我的 AngularJS 應用程序的控制器部分,文件在輸入的更改事件中處理,如下所示:
In the controller part of my AngularJS app the file is processed inside the input's change event like so:
//bind the change event of the file input and process the selected file
inputElement.on("change", function (evt) {
var fileList = evt.target.files;
var selectedFile = fileList[0];
if (selectedFile.size > 500000) {
alert('File too big!');
// ...
我希望 evt.target.files
在我的單元測試中包含我的模擬數據,而不是用戶選擇的文件.我意識到我自己無法實例化 FileList
和 File
對象,這將是瀏覽器正在使用的相應對象.因此,我將模擬 FileList 分配給輸入的 files
屬性并手動觸發更改事件:
I'd like evt.target.files
to contain my mock data instead of the user's selected file in my unit test. I realized that I can't instantiate a FileList
and File
object by myself, which would be the according objects the browser is working with. So I went with assigning a mock FileList to the input's files
property and triggering the change event manually:
describe('document upload:', function () {
var input;
beforeEach(function () {
input = angular.element("<input type='file' id='file' accept='image/*'>");
spyOn(document, 'getElementById').andReturn(input);
createController();
});
it('should check file size of the selected file', function () {
var file = {
name: "test.png",
size: 500001,
type: "image/png"
};
var fileList = {
0: file,
length: 1,
item: function (index) { return file; }
};
input.files = fileList; // assign the mock files to the input element
input.triggerHandler("change"); // trigger the change event
expect(window.alert).toHaveBeenCalledWith('File too big!');
});
不幸的是,這會導致控制器中出現以下錯誤,表明此嘗試失敗,因為文件根本沒有分配給輸入元素:
Unfortunately, this causes the following error in the controller which shows that this attempt failed because the files were not assigned to the input element at all:
TypeError: 'undefined' 不是對象(評估 'evt.target.files')
出于安全原因,我已經發現 input.files
屬性是只讀的.所以我開始了另一種方法,通過調度一個定制的更改來提供 files 屬性,但仍然沒有成功.
I already found out that the input.files
property is read-only for security reasons. So I started another approach by dispatching a customized change which would provide the files property, but still without success.
長話短說:我很想學習一個可行的解決方案或任何關于如何處理這個測試用例的最佳實踐.
推薦答案
讓我們重新思考 AngularJS,DOM 必須在指令中處理
Let's rethink AngularJS, DOM must be handled in a directive
我們不應該在控制器中處理 DOM 元素,即 element.on('change', ..
,尤其是為了測試目的.在控制器中,您與數據交談,而不是與 DOM.
We should not deal with DOM element in a controller, i.e. element.on('change', ..
, especially for testing purpose. In a controller, You talk to data, not to DOM.
因此,那些 onchange
應該是如下的指令
Thus, those onchange
should be a directive like the following
<input type="file" name='file' ng-change="fileChanged()" /> <br/>
但是,不幸的是,ng-change
不適用于 type="file"
.我不確定未來的版本是否適用于此.我們仍然可以應用相同的方法.
However, unfortunately, ng-change
does not work well with type="file"
. I am not sure that the future version works with this or not. We still can apply the same method though.
<input type="file"
onchange="angular.element(this).scope().fileChanged(this.files)" />
在控制器中,我們只是簡單地定義一個方法
and in the controller, we just simply define a method
$scope.fileChanged = function(files) {
return files.0.length < 500000;
};
現在,一切都只是普通的控制器測試.不再需要處理 angular.element
、$compile
、triggers
等!:)
Now, everything is just a normal controller test. No more dealing with angular.element
, $compile
, triggers
, etc.! :)
describe(‘MyCtrl’, function() {
it('does check files', inject(
function($rootScope, $controller) {
scope = $rootScope.new();
ctrl = $controller(‘UploadCtrl’, {‘$scope’: scope});
var files = { 0: {name:'foo', size: 500001} };
expect(scope.fileChanged(files)).toBe(true);
}
));
});
http://plnkr.co/edit/1J7ETus0etBLO18FQDhK?p=preview
這篇關于如何提供模擬文件來更改 <input type='file'> 的事件用于單元測試的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!