admin管理员组文章数量:1389758
I'm adding some unit tests to my angular 7 app but I'm not sure how to handle the interaction with a FileReader since using async/whenStable, fakeAsync and promises don't work as expected.
- The behavior I'm testing: I need to verify the service
documentService.sendDocument
is called once a file is uploaded.
1st Approach:
Before I found issues testing FileReader.onload I just tried to test it without async.
Controller Code
onFileGovernmentIdChange(event) {
console.log('::: Init onFileGovernmentIdChange');
const updateFunction = () => {
console.log('::: BEFORE update fileGovernmentIdUploaded - ', this.fileGovernmentIdUploaded);
this.fileGovernmentIdUploaded = true;
console.log('::: AFTER update fileGovernmentIdUploaded - ', this.fileGovernmentIdUploaded);
}
this.saveFileFromInputSimple(event, DOCUMENT_TYPE.STORE_GOVERNMENT_ID, updateFunction);
console.log('::: End onFileGovernmentIdChange');
}
private saveFileFromInputSimple(event, documentType: DOCUMENT_TYPE, updateState: () => any) {
console.log('::: Init saveFileFromInputSimple');
const reader = new FileReader();
if (event.target.files && event.target.files.length > 0) {
console.log('::: Event with files...');
const file = event.target.files[0];
reader.onload = () => {
this.documentService
.sendDocument(file.name, file.type, reader.result.toString(), documentType)
.subscribe(
response => {
console.log('::: sendDocument - Subscribe OK');
updateState()
},
error => {
console.log('::: sendDocument - Subscribe ERROR');
this.showDefaultErrorDialog()
}
);
};
console.log('::: Onload callback assigned...');
reader.readAsDataURL(file);
}
console.log('::: End saveFileFromInputSimple');
}
UnitTest spec code
fit('simple testing', () => {
const documentService = TestBed.get(DocumentsService);
const catalogService: CatalogService = TestBed.get(CatalogService);
const customEvent = {
target: {
files: [new Blob(['ssdfsdgdjghdslkjghdjg'], { type: 'pdf' })]
}
};
const merceResponse = new CommerceResponse();
merceResponsemissionPercentage = '11';
spyOn(catalogService, 'getCatalog').and.returnValue(of({ catalogs: [] }));
spyOn(documentService, 'getCommerceInfo').and.returnValue(of(merceResponse));
spyOn(documentService, 'sendDocument').and.returnValue(of({ response: 'ok' }));
fixture.detectChanges();//Apply onInit changes
console.log('::: Before calling onFileGovernmentIdChange()');
ponent.onFileGovernmentIdChange(customEvent);
console.log('::: After calling onFileGovernmentIdChange()');
console.log('::: Before expects... ');
expect(ponent.fileGovernmentIdUploaded).toBeTruthy();
// expect(documentService.sendDocument).toHaveBeenCalledTimes(1);
console.log('::: After expects... ');
});
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()'
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputSimple'
LOG: '::: Event with files...'
LOG: '::: Onload callback assigned...'
LOG: '::: End saveFileFromInputSimple'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After expects... '
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent simple testing FAILED
Expected undefined to be truthy.
at UserContext.<anonymous> src/app/documents/documentsponent.spec.ts:146:52)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:288:1)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:387:1)
UnitTest analysis
The code inside the onload
method is never executed
2nd Approach
Testing FileReader.onload without Observers: Just to follow the workflow I removed the Observer inside the onload
to verify if that could be the problem.
Controller Code
private saveFileFromInputSimple(event, documentType: DOCUMENT_TYPE, updateState: () => any) {
console.log('::: Init saveFileFromInputSimple');
const reader = new FileReader();
if (event.target.files && event.target.files.length > 0) {
console.log('::: Event with files...');
const file = event.target.files[0];
reader.onload = () => {
console.log('::: ONLOAD executed');
};
console.log('::: Onload callback assigned...');
reader.readAsDataURL(file);
}
console.log('::: End saveFileFromInputSimple');
}
UnitTest spec code
Same as 1st approach
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()'
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputSimple'
LOG: '::: Event with files...'
LOG: '::: Onload callback assigned...'
LOG: '::: End saveFileFromInputSimple'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After expects... '
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent simple testing FAILED
Expected undefined to be truthy.
at UserContext.<anonymous> src/app/documents/documentsponent.spec.ts:146:52)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:288:1)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:387:1)
Chrome 71.0.3578 (Mac OS X 10.13.6): Executed 1 of 46 (1 FAILED) (0 secs / 0.314 secs)
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent simple testing FAILED
Expected undefined to be truthy.
at UserContext.<anonymous> src/app/documents/documentsponent.spec.ts:146:52)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:288:1)
LOG: '::: ONLOAD executed'
UnitTest analysis
Now the code in the onload
method is executed but looks like an async operation because it was executed at the end.
3rd Approach
Testing FileReader.onload with async unit test: As I found some kind of async operation I included the async/fixture.whenStable
angular bination to wait for async code pletion.
Controller Code
Same as 2nd approach
UnitTest spec code
fit('Testing with async/fixture.whenStable', async(() => {
const documentService = TestBed.get(DocumentsService);
const catalogService: CatalogService = TestBed.get(CatalogService);
const customEvent = {
target: {
files: [new Blob(['ssdfsdgdjghdslkjghdjg'], { type: 'pdf' })]
}
};
const merceResponse = new CommerceResponse();
merceResponsemissionPercentage = '11';
spyOn(catalogService, 'getCatalog').and.returnValue(of({ catalogs: [] }));
spyOn(documentService, 'getCommerceInfo').and.returnValue(of(merceResponse));
spyOn(documentService, 'sendDocument').and.returnValue(of({ response: 'ok' }));
fixture.detectChanges();//Apply onInit changes
console.log('::: Before calling onFileGovernmentIdChange()');
ponent.onFileGovernmentIdChange(customEvent);
console.log('::: After calling onFileGovernmentIdChange()');
console.log('::: Before expects... ');
fixture.whenStable().then(() => {
fixture.detectChanges();
console.log('::: whenStable Init');
expect(ponent.fileGovernmentIdUploaded).toBeTruthy();
// expect(documentService.sendDocument).toHaveBeenCalledTimes(1);
console.log('::: whenStable End');
});
console.log('::: After expects... ');
}));
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()'
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputSimple'
LOG: '::: Event with files...'
LOG: '::: Onload callback assigned...'
LOG: '::: End saveFileFromInputSimple'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After expects... '
LOG: '::: whenStable Init'
LOG: '::: whenStable End'
**LOG: '::: ONLOAD executed'**
UnitTest analysis
As expected, the whenStable code is executed when the method has finished, however, the onload
method continues executing at the end.
Googling I found that could be better to wrap the onload part in a Promise to be sure it's tracked by Angular async.
4th Approach:
Wrap the onload
in a promise and verify it works with the async/whenStable
structure.
Controller Code
private readFileAsync(file): Promise<string> {
console.log('::: readFileAsync Init');
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
console.log(':::: Promise Resolved in ONLOAD')
resolve(reader.result.toString());
}
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
private saveFileFromInputWithPromise(event, documentType: DOCUMENT_TYPE, updateState: () => any) {
console.log('::: Init saveFileFromInputWithPromise');
if (event.target.files && event.target.files.length > 0) {
console.log('::: Event with files...');
const file = event.target.files[0];
this.readFileAsync(file)
.then(fileContent => {
console.log('::: File with content', fileContent);
updateState();
}).catch(error => console.log('::: File load error', error));
}
console.log('::: End saveFileFromInputWithPromise');
}
UnitTest spec code
Same as 3rd approach
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()'
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputWithPromise'
LOG: '::: Event with files...'
LOG: '::: readFileAsync Init'
LOG: '::: End saveFileFromInputWithPromise'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After expects... '
LOG: '::: whenStable Init'
LOG: '::: whenStable End'
LOG: ':::: Promise Resolved in ONLOAD'
LOG: '::: File with content', 'data:pdf;base64,c3NkZnNkZ2RqZ2hkc2xramdoZGpn'
LOG: '::: BEFORE update fileGovernmentIdUploaded - ', undefined
LOG: '::: AFTER update fileGovernmentIdUploaded - ', true
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent Testing with async/fixture.whenStable FAILED
Expected undefined to be truthy.
at http://localhost:9877/_karma_webpack_/webpack:/src/app/documents/documentsponent.spec.ts:176:56
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at AsyncTestZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.AsyncTestZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:713:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:285:1)
UnitTest analysis
Again the Promise is resolved after the whenStable
function.
5th Approach:
As the async/whenStable
didn't work as expected I tried changing to fakeAsync/tick/flush
structure.
Controller Code
Same as 4th Approach (Including onload
in a promise)
UnitTest spec code
fit('Testing with fakeAsync/tick/flush', fakeAsync(() => {
const documentService = TestBed.get(DocumentsService);
const catalogService: CatalogService = TestBed.get(CatalogService);
const customEvent = {
target: {
files: [new Blob(['ssdfsdgdjghdslkjghdjg'], { type: 'pdf' })]
}
};
const merceResponse = new CommerceResponse();
merceResponsemissionPercentage = '11';
spyOn(catalogService, 'getCatalog').and.returnValue(of({ catalogs: [] }));
spyOn(documentService, 'getCommerceInfo').and.returnValue(of(merceResponse));
spyOn(documentService, 'sendDocument').and.returnValue(of({ response: 'ok' }));
fixture.detectChanges();
console.log('::: Before calling onFileGovernmentIdChange()');
ponent.onFileGovernmentIdChange(customEvent);
console.log('::: After calling onFileGovernmentIdChange()');
console.log('::: Before expects... ');
fixture.detectChanges();
tick();
flushMicrotasks();
flush();
console.log('::: After Flush Init');
expect(ponent.fileGovernmentIdUploaded).toBeTruthy();
// expect(documentService.sendDocument).toHaveBeenCalledTimes(1);
console.log('::: After Flush End');
console.log('::: After expects... ');
}));
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputWithPromise'
LOG: '::: Event with files...'
LOG: '::: readFileAsync Init'
LOG: '::: End saveFileFromInputWithPromise'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After Flush Init'
LOG: '::: After Flush End'
LOG: '::: After expects... '
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent Testing with async/fixture.whenStable FAILED
Expected undefined to be truthy.
at UserContext.<anonymous> src/app/documents/documentsponent.spec.ts:211:52)
at UserContext.<anonymous> node_modules/zone.js/dist/zone-testing.js:1424:1)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:288:1)
LOG: ':::: Promise Resolved in ONLOAD'
LOG: '::: File with content', 'data:pdf;base64,c3NkZnNkZ2RqZ2hkc2xramdoZGpn'
LOG: '::: BEFORE update fileGovernmentIdUploaded - ', undefined
LOG: '::: AFTER update fileGovernmentIdUploaded - ', true
UnitTest analysis
Again, the Promise is resolved after the fakeAsync/tick/flushMicrotasks/flush structure
What is wrong?
I followed every tutorial I found and every different approach trying to include the FileReader.onload
in my test (as that method call the service I want to spy and verify) but always the method resolve after the async blocks provided by Angular. I saw other approaches where the window.fileReader
is mocked but that's not the purpose of my test.
So could anybody tell me what is wrong in my code or in the way I'm testing?
I'm adding some unit tests to my angular 7 app but I'm not sure how to handle the interaction with a FileReader since using async/whenStable, fakeAsync and promises don't work as expected.
- The behavior I'm testing: I need to verify the service
documentService.sendDocument
is called once a file is uploaded.
1st Approach:
Before I found issues testing FileReader.onload I just tried to test it without async.
Controller Code
onFileGovernmentIdChange(event) {
console.log('::: Init onFileGovernmentIdChange');
const updateFunction = () => {
console.log('::: BEFORE update fileGovernmentIdUploaded - ', this.fileGovernmentIdUploaded);
this.fileGovernmentIdUploaded = true;
console.log('::: AFTER update fileGovernmentIdUploaded - ', this.fileGovernmentIdUploaded);
}
this.saveFileFromInputSimple(event, DOCUMENT_TYPE.STORE_GOVERNMENT_ID, updateFunction);
console.log('::: End onFileGovernmentIdChange');
}
private saveFileFromInputSimple(event, documentType: DOCUMENT_TYPE, updateState: () => any) {
console.log('::: Init saveFileFromInputSimple');
const reader = new FileReader();
if (event.target.files && event.target.files.length > 0) {
console.log('::: Event with files...');
const file = event.target.files[0];
reader.onload = () => {
this.documentService
.sendDocument(file.name, file.type, reader.result.toString(), documentType)
.subscribe(
response => {
console.log('::: sendDocument - Subscribe OK');
updateState()
},
error => {
console.log('::: sendDocument - Subscribe ERROR');
this.showDefaultErrorDialog()
}
);
};
console.log('::: Onload callback assigned...');
reader.readAsDataURL(file);
}
console.log('::: End saveFileFromInputSimple');
}
UnitTest spec code
fit('simple testing', () => {
const documentService = TestBed.get(DocumentsService);
const catalogService: CatalogService = TestBed.get(CatalogService);
const customEvent = {
target: {
files: [new Blob(['ssdfsdgdjghdslkjghdjg'], { type: 'pdf' })]
}
};
const merceResponse = new CommerceResponse();
merceResponse.missionPercentage = '11';
spyOn(catalogService, 'getCatalog').and.returnValue(of({ catalogs: [] }));
spyOn(documentService, 'getCommerceInfo').and.returnValue(of(merceResponse));
spyOn(documentService, 'sendDocument').and.returnValue(of({ response: 'ok' }));
fixture.detectChanges();//Apply onInit changes
console.log('::: Before calling onFileGovernmentIdChange()');
ponent.onFileGovernmentIdChange(customEvent);
console.log('::: After calling onFileGovernmentIdChange()');
console.log('::: Before expects... ');
expect(ponent.fileGovernmentIdUploaded).toBeTruthy();
// expect(documentService.sendDocument).toHaveBeenCalledTimes(1);
console.log('::: After expects... ');
});
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()'
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputSimple'
LOG: '::: Event with files...'
LOG: '::: Onload callback assigned...'
LOG: '::: End saveFileFromInputSimple'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After expects... '
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent simple testing FAILED
Expected undefined to be truthy.
at UserContext.<anonymous> src/app/documents/documents.ponent.spec.ts:146:52)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:288:1)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:387:1)
UnitTest analysis
The code inside the onload
method is never executed
2nd Approach
Testing FileReader.onload without Observers: Just to follow the workflow I removed the Observer inside the onload
to verify if that could be the problem.
Controller Code
private saveFileFromInputSimple(event, documentType: DOCUMENT_TYPE, updateState: () => any) {
console.log('::: Init saveFileFromInputSimple');
const reader = new FileReader();
if (event.target.files && event.target.files.length > 0) {
console.log('::: Event with files...');
const file = event.target.files[0];
reader.onload = () => {
console.log('::: ONLOAD executed');
};
console.log('::: Onload callback assigned...');
reader.readAsDataURL(file);
}
console.log('::: End saveFileFromInputSimple');
}
UnitTest spec code
Same as 1st approach
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()'
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputSimple'
LOG: '::: Event with files...'
LOG: '::: Onload callback assigned...'
LOG: '::: End saveFileFromInputSimple'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After expects... '
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent simple testing FAILED
Expected undefined to be truthy.
at UserContext.<anonymous> src/app/documents/documents.ponent.spec.ts:146:52)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:288:1)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:387:1)
Chrome 71.0.3578 (Mac OS X 10.13.6): Executed 1 of 46 (1 FAILED) (0 secs / 0.314 secs)
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent simple testing FAILED
Expected undefined to be truthy.
at UserContext.<anonymous> src/app/documents/documents.ponent.spec.ts:146:52)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:288:1)
LOG: '::: ONLOAD executed'
UnitTest analysis
Now the code in the onload
method is executed but looks like an async operation because it was executed at the end.
3rd Approach
Testing FileReader.onload with async unit test: As I found some kind of async operation I included the async/fixture.whenStable
angular bination to wait for async code pletion.
Controller Code
Same as 2nd approach
UnitTest spec code
fit('Testing with async/fixture.whenStable', async(() => {
const documentService = TestBed.get(DocumentsService);
const catalogService: CatalogService = TestBed.get(CatalogService);
const customEvent = {
target: {
files: [new Blob(['ssdfsdgdjghdslkjghdjg'], { type: 'pdf' })]
}
};
const merceResponse = new CommerceResponse();
merceResponse.missionPercentage = '11';
spyOn(catalogService, 'getCatalog').and.returnValue(of({ catalogs: [] }));
spyOn(documentService, 'getCommerceInfo').and.returnValue(of(merceResponse));
spyOn(documentService, 'sendDocument').and.returnValue(of({ response: 'ok' }));
fixture.detectChanges();//Apply onInit changes
console.log('::: Before calling onFileGovernmentIdChange()');
ponent.onFileGovernmentIdChange(customEvent);
console.log('::: After calling onFileGovernmentIdChange()');
console.log('::: Before expects... ');
fixture.whenStable().then(() => {
fixture.detectChanges();
console.log('::: whenStable Init');
expect(ponent.fileGovernmentIdUploaded).toBeTruthy();
// expect(documentService.sendDocument).toHaveBeenCalledTimes(1);
console.log('::: whenStable End');
});
console.log('::: After expects... ');
}));
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()'
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputSimple'
LOG: '::: Event with files...'
LOG: '::: Onload callback assigned...'
LOG: '::: End saveFileFromInputSimple'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After expects... '
LOG: '::: whenStable Init'
LOG: '::: whenStable End'
**LOG: '::: ONLOAD executed'**
UnitTest analysis
As expected, the whenStable code is executed when the method has finished, however, the onload
method continues executing at the end.
Googling I found that could be better to wrap the onload part in a Promise to be sure it's tracked by Angular async.
4th Approach:
Wrap the onload
in a promise and verify it works with the async/whenStable
structure.
Controller Code
private readFileAsync(file): Promise<string> {
console.log('::: readFileAsync Init');
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
console.log(':::: Promise Resolved in ONLOAD')
resolve(reader.result.toString());
}
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
private saveFileFromInputWithPromise(event, documentType: DOCUMENT_TYPE, updateState: () => any) {
console.log('::: Init saveFileFromInputWithPromise');
if (event.target.files && event.target.files.length > 0) {
console.log('::: Event with files...');
const file = event.target.files[0];
this.readFileAsync(file)
.then(fileContent => {
console.log('::: File with content', fileContent);
updateState();
}).catch(error => console.log('::: File load error', error));
}
console.log('::: End saveFileFromInputWithPromise');
}
UnitTest spec code
Same as 3rd approach
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()'
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputWithPromise'
LOG: '::: Event with files...'
LOG: '::: readFileAsync Init'
LOG: '::: End saveFileFromInputWithPromise'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After expects... '
LOG: '::: whenStable Init'
LOG: '::: whenStable End'
LOG: ':::: Promise Resolved in ONLOAD'
LOG: '::: File with content', 'data:pdf;base64,c3NkZnNkZ2RqZ2hkc2xramdoZGpn'
LOG: '::: BEFORE update fileGovernmentIdUploaded - ', undefined
LOG: '::: AFTER update fileGovernmentIdUploaded - ', true
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent Testing with async/fixture.whenStable FAILED
Expected undefined to be truthy.
at http://localhost:9877/_karma_webpack_/webpack:/src/app/documents/documents.ponent.spec.ts:176:56
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at AsyncTestZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.AsyncTestZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:713:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:285:1)
UnitTest analysis
Again the Promise is resolved after the whenStable
function.
5th Approach:
As the async/whenStable
didn't work as expected I tried changing to fakeAsync/tick/flush
structure.
Controller Code
Same as 4th Approach (Including onload
in a promise)
UnitTest spec code
fit('Testing with fakeAsync/tick/flush', fakeAsync(() => {
const documentService = TestBed.get(DocumentsService);
const catalogService: CatalogService = TestBed.get(CatalogService);
const customEvent = {
target: {
files: [new Blob(['ssdfsdgdjghdslkjghdjg'], { type: 'pdf' })]
}
};
const merceResponse = new CommerceResponse();
merceResponse.missionPercentage = '11';
spyOn(catalogService, 'getCatalog').and.returnValue(of({ catalogs: [] }));
spyOn(documentService, 'getCommerceInfo').and.returnValue(of(merceResponse));
spyOn(documentService, 'sendDocument').and.returnValue(of({ response: 'ok' }));
fixture.detectChanges();
console.log('::: Before calling onFileGovernmentIdChange()');
ponent.onFileGovernmentIdChange(customEvent);
console.log('::: After calling onFileGovernmentIdChange()');
console.log('::: Before expects... ');
fixture.detectChanges();
tick();
flushMicrotasks();
flush();
console.log('::: After Flush Init');
expect(ponent.fileGovernmentIdUploaded).toBeTruthy();
// expect(documentService.sendDocument).toHaveBeenCalledTimes(1);
console.log('::: After Flush End');
console.log('::: After expects... ');
}));
UnitTest result
LOG: '::: Before calling onFileGovernmentIdChange()
LOG: '::: Init onFileGovernmentIdChange'
LOG: '::: Init saveFileFromInputWithPromise'
LOG: '::: Event with files...'
LOG: '::: readFileAsync Init'
LOG: '::: End saveFileFromInputWithPromise'
LOG: '::: End onFileGovernmentIdChange'
LOG: '::: After calling onFileGovernmentIdChange()'
LOG: '::: Before expects... '
LOG: '::: After Flush Init'
LOG: '::: After Flush End'
LOG: '::: After expects... '
Chrome 71.0.3578 (Mac OS X 10.13.6) DocumentsComponent Testing with async/fixture.whenStable FAILED
Expected undefined to be truthy.
at UserContext.<anonymous> src/app/documents/documents.ponent.spec.ts:211:52)
at UserContext.<anonymous> node_modules/zone.js/dist/zone-testing.js:1424:1)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke node_modules/zone.js/dist/zone-testing.js:288:1)
LOG: ':::: Promise Resolved in ONLOAD'
LOG: '::: File with content', 'data:pdf;base64,c3NkZnNkZ2RqZ2hkc2xramdoZGpn'
LOG: '::: BEFORE update fileGovernmentIdUploaded - ', undefined
LOG: '::: AFTER update fileGovernmentIdUploaded - ', true
UnitTest analysis
Again, the Promise is resolved after the fakeAsync/tick/flushMicrotasks/flush structure
What is wrong?
I followed every tutorial I found and every different approach trying to include the FileReader.onload
in my test (as that method call the service I want to spy and verify) but always the method resolve after the async blocks provided by Angular. I saw other approaches where the window.fileReader
is mocked but that's not the purpose of my test.
So could anybody tell me what is wrong in my code or in the way I'm testing?
Share Improve this question edited Feb 11, 2019 at 16:53 Hernán Tenjo asked Feb 10, 2019 at 0:43 Hernán TenjoHernán Tenjo 1982 silver badges13 bronze badges1 Answer
Reset to default 6Great post, perfectly describing my evening.
It seems it is a known issue that fakeAsync does not support FileReader, as "it is not fundamentaly a timer related async operation".
See: https://github./angular/zone.js/issues/1120
本文标签:
版权声明:本文标题:javascript - Angular Unit Test including FileReader.onload doesn't work with asyncwhenStable nor fakeAsynctick - Stack O 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744742299a2622679.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论