输入 [type="file"] 的 Angular 和 Jasmine 单元测试更改事件
Posted
技术标签:
【中文标题】输入 [type="file"] 的 Angular 和 Jasmine 单元测试更改事件【英文标题】:Angular & Jasmine Unit Test change event for input[type="file"] 【发布时间】:2019-01-28 22:34:41 【问题描述】:我正在尝试 100% 的测试覆盖率,但我似乎无法测试这个 onUploadFile 函数中的内容。
html 模板
<input type="file" formControlName="theUpload" id="upload" (change)="onUploadFile($event, i, true)">
filing.ts 文件
onUploadFile(evt: Event, index: number, isReq: boolean = false): void
const reader = new FileReader();
const target = <HTMLInputElement>evt.target;
if (target.files && target.files.length)
const file = target.files[0];
reader.readAsDataURL(file);
reader.onload = () =>
this.getUploadFormGroup(index, isReq).patchValue(
filename: file.name,
filetype: file.type,
value: reader.result.split(',')[1],
dateUploaded: new Date()
);
console.log(
`getUploadFormArray ($isReq ? 'required' : 'other' forms)`,
this.getUploadFormArray(isReq)
);
;
filing.spec.ts 文件
it('should call onUploadFile when input is changed for required files', () =>
spyOn(component, 'onUploadFile').and.callThrough();
const fakeChangeEvent = new Event('change');
const targ = <HTMLInputElement>de.nativeElement.querySelector('input#upload');
targ.dispatchEvent(fakeChangeEvent);
fixture.whenStable().then(() =>
expect(component.onUploadFile).toHaveBeenCalledWith(fakeChangeEvent, 0, true);
expect(targ.files.length).toBeGreaterThan(0); // this is always zero because i can't add to targ.files (readonly FileList)
);
);
我愿意模拟任何可以模拟的东西,但有人可以告诉我如何测试console.log
函数(要求我在target.files
数组中至少有一项?
【问题讨论】:
【参考方案1】:让我们遵循分而治之的规则 - 因为单元测试的目的是分别测试组件逻辑的各个部分,所以我会稍微改变组件逻辑以使单元测试更容易.
您在 onUploadFile
方法中为 onload
设置匿名回调函数,因此无法窥探它。作为权衡 - 您可以重构回调方法:
export class TestComponent
...
getLoadCallback(fg: FormGroup, file: File, reader: FileReader): () => void
return () =>
fg.patchValue(
filename: file.name,
filetype: file.type,
value: reader.result.split(',')[1],
dateUploaded: new Date()
);
// do some other stuff here
;
onUploadFile(evt: Event, index: number, isReq: boolean = false): void
const reader = new FileReader();
const target = <HTMLInputElement>evt.target;
if (target.files && target.files.length)
const file = target.files[0];
reader.readAsDataURL(file);
const fg = this.getUploadFormGroup(index, isReq);
reader.onload = this.getLoadCallback(fg, file, reader);
...
所以,现在我们可以创建至少三个单元测试:一个用于在输入事件上触发onUploadFile
(您现有的测试对此很有用),另一个用于测试getLoadCallback
方法,最后一个用于onUploadFile
方法:
it('getLoadCallback', () =>
const mockValue = ['a', 'b'];
const result = jasmine.createSpyObj('result', ['split']);
result.split.and.callFake(() => mockValue);
const mockReader = result as FileReader;
const mockFormGroup: FormGroup = jasmine.createSpyObj('FormGroup', ['patchValue']);
const mockFile = new File([''], 'filename', type: 'text/html' );
const callback: () => void = component.getLoadCallback(mockFormGroup, mockFile, mockReader);
callback();
const obj =
filename: mockFile.name,
filetype: mockFile.type,
value: mockValue[1],
dateUploaded: new Date()
expect(mockFormGroup.patchValue).toHaveBeenCalledWith(obj);
);
it('onUploadFile', () =>
const mockIndex = 1;
const mockIsReq = false;
const mockFile = new File([''], 'filename', type: 'text/html' );
const mockFormGroup = new FormGroup();
const mockEvt = target: files: [mockFile] ;
const mockReader: FileReader = jasmine.createSpyObj('FileReader', ['readAsDataURL', 'onload']);
spyOn(window as any, 'FileReader').and.returnValue(mockReader);
spyOn(component, 'getUploadFormGroup').and.returnValue(mockFormGroup);
spyOn(component, 'getLoadCallback').and.callThrough();
component.onUploadFile(mockEvt as any, mockIndex, mockIsReq);
expect((window as any).FileReader).toHaveBeenCalled();
expect(mockReader.readAsDataURL).toHaveBeenCalledWith(mockFile);
expect(component.getUploadFormGroup).toHaveBeenCalledWith(mockIndex, mockIsReq);
expect(component.getLoadCallback).toHaveBeenCalledWith(mockFormGroup, mockFile, mockReader);
);
当然,您可以更改模拟值并创建更多单元测试...
【讨论】:
我也创建了stackblitz example。以上是关于输入 [type="file"] 的 Angular 和 Jasmine 单元测试更改事件的主要内容,如果未能解决你的问题,请参考以下文章
<input type="file">,输入的信息怎样用jquery获取?
使用 jquery 验证引擎插件时输入 type="file" 不发送文件
如何使用 ng-model 双向绑定到 type="file" 输入?
我想在 boostrap4 的输入 type="file" 中显示所选文件的名称 [重复]