I have an Angular component and am testing a function call. The function calls a service function and I am trying to test that the service function is being called correctly.
Component:
loadAllDocuments( event = null ): void {
this.clientDocService.setDocuments([]);
const key = `clients/documents/000/000/${this.clientId}/original`;
this.s3.getBucketContents(key).then( data => {
const contentLength = data.Contents.length
let foundLength = 0
let missingLength = 0
for ( const entry of data.Contents ) {
const dataParams = {name: entry.name}
this.clientDocService.addToDocuments(dataParams)
this.clientDocService.createClientDocIfMissing(dataParams).subscribe(doc => {
doc ? missingLength = 1 : foundLength = 1
})
if (missingLength foundLength === contentLength) {
const message = `Found ${contentLength} documents in storage. ${foundLength} were already here and ${missingLength} were new`
// What I am testing
this.global.handleResponse(message)
// logs 'Found 1 documents in storage. 0 were already here and 1 were new' to console
console.log(message)
}
}
})
if (event) event.target.complete();
}
My test:
describe('DocumentsPage', () => {
let component: DocumentsPage;
let fixture: ComponentFixture<DocumentsPage>;
const mockGlobalsService = jasmine.createSpyObj('GlobalsService', ['base_url', 'handleResponse'])
const mockS3Service = jasmine.createSpyObj('S3_Service', ['getBucketContents'])
const mockClientDocService = jasmine.createSpyObj('ClientDocService', ['setDocuments', 'createClientDocIfMissing', 'addToDocuments', 'addToAWSDocs', 'fetchClientAndSignedDocuments', 'setMergedDocuments'])
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ DocumentsPage ],
imports: [HttpClientTestingModule,],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
{ provide: GlobalsService, useValue: mockGlobalsService },
{ provide: S3_Service, useValue: mockS3Service },
ExternalDocumentsService,
{ provide: ClientDocService, useValue: mockClientDocService },
]
})
.compileComponents();
}));
beforeEach(() => {
mockClientDocService.mergedDocuments$ = of([])
fixture = TestBed.createComponent(DocumentsPage);
component = fixture.componentInstance;
service = TestBed.inject(ClientDocService)
fixture.detectChanges();
});
describe('loadAllDocuments', () => {
it('', () => {
const mockBucketContents = {
Contents: [
{name: 'test 1', Key: 'x'},
]
}
mockS3Service.getBucketContents.and.returnValue(new Promise((res) => res(mockBucketContents)))
mockClientDocService.createClientDocIfMissing.and.returnValue(of([{name: 'test 2', Key: 'y'}]))
component.loadAllDocuments()
// Fails despite value being logged
expect(mockGlobalsService.handleResponse).toHaveBeenCalledOnceWith('Found 1 documents in storage. 0 were already here and 1 were new')
})
})
});
The test fails with response:
Expected spy GlobalsService.handleResponse to have been called only once, and with given args:
[ 'Found 1 documents in storage. 0 were already here and 1 were new' ]
But it was never called.
I'm not sure if I'm mocking the call incorrectly or something?
CodePudding user response:
This has to do with the asynchronous nature of the code, the .then
.
You see it being logged out but it's being logged out at a later point in time, after your assertion.
One way to fix it would be to use async/fixture.whenStable()
.
// !! add async here
describe('loadAllDocuments', () => {
it('', async () => {
const mockBucketContents = {
Contents: [
{name: 'test 1', Key: 'x'},
]
}
mockS3Service.getBucketContents.and.returnValue(new Promise((res) => res(mockBucketContents)))
mockClientDocService.createClientDocIfMissing.and.returnValue(of([{name: 'test 2', Key: 'y'}]))
component.loadAllDocuments();
// !! wait until all pending promises have completed before continuing
await fixture.whenStable();
// Fails despite value being logged
expect(mockGlobalsService.handleResponse).toHaveBeenCalledOnceWith('Found 1 documents in storage. 0 were already here and 1 were new')
})
})
Another way to solve it would be to use fakeAsync/tick
.
// !! Wrap callback with fakeAsync, pay attention to the extra brackets
describe('loadAllDocuments', () => {
it('', fakeAsync(() => {
const mockBucketContents = {
Contents: [
{name: 'test 1', Key: 'x'},
]
}
mockS3Service.getBucketContents.and.returnValue(new Promise((res) => res(mockBucketContents)))
mockClientDocService.createClientDocIfMissing.and.returnValue(of([{name: 'test 2', Key: 'y'}]))
component.loadAllDocuments()
// !! call tick to say finish all pending promises before continuing
tick();
// Fails despite value being logged
expect(mockGlobalsService.handleResponse).toHaveBeenCalledOnceWith('Found 1 documents in storage. 0 were already here and 1 were new')
}))
})