Home > OS >  Angular testing: Test fails despite function clearly being called
Angular testing: Test fails despite function clearly being called

Time:11-19

I am testing a component and I cannot figure out why a test is failing. The console.log() clearly shows the function being called. I think it has something to do with the subscribe() call but am not sure how to correct my test:

My component:

async deleteDocument( document : IMergedDocument ) {
  const key = document.fileKey;
  const className = document.className

  const confirm = await this.modalCtrl.create({
    component: AwakenModal,
    componentProps: {
      title: `Are you sure you want to delete ${document.name}?`,
      subtitle: 'Deletions are irreversible.',
      type: 'confirm',
      urgent: true
    },
    cssClass: 'small-modal'
  })

  await confirm.present()
  const data = await confirm.onDidDismiss()

  if (data?.data === 'yes') {
    if (className === 'ClientDoc') {
      this.s3.deleteFromS3(key).then(() => {

        // What I am testing.  I see this in console
        this.clientDocService.destroy(document.id).subscribe(d => console.log('doc', d))
      })
        .catch(err => this.global.handleResponse(`Error: ${err}`))
    } else if (className === 'SignedDocument') {
      this.clientDocService.unlinkSignedDocument(document).subscribe(
        () => this.global.handleResponse('Successfully unlinked the document from this profile', false, 'success'),
        err => this.global.handleResponse(err.error, true)
      )
    }
  }
}

My test:

describe('DocumentsPage', () => {
  let component: DocumentsPage;
  let fixture: ComponentFixture<DocumentsPage>;
  const mockS3Service = jasmine.createSpyObj('S3_Service', ['getBucketContents', 'deleteFromS3'])
  const mockModalController = jasmine.createSpyObj('ModalController', ['create'])
  const mockClientDocService = jasmine.createSpyObj('ClientDocService',
    ['destroy', 'setDocuments', 'createClientDocIfMissing', 'addToDocuments', 'addToAWSDocs', 'fetchClientAndSignedDocuments', 'setMergedDocuments']
  )

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ DocumentsPage ],
      imports: [HttpClientTestingModule,],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      providers: [
        { provide: S3_Service, useValue: mockS3Service },
        { provide: ModalController, useValue: mockModalController },
        ExternalDocumentsService,
        { provide: ClientDocService, useValue: mockClientDocService },
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    mockClientDocService.mergedDocuments$ = of([])
    fixture = TestBed.createComponent(DocumentsPage);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  describe('deleteDocument', () => {
    it('should call clientDocService.destroy when a ClientDoc', async() => {
      const doc = {
        id: 1,
        className: 'ClientDoc',
        canBeViewed: true
      }

      const modalSpy = jasmine.createSpyObj('Modal', ['present', 'onDidDismiss'])
      mockModalController.create.and.callFake(() => modalSpy)
      modalSpy.onDidDismiss.and.returnValue({data: 'yes'})
      mockS3Service.deleteFromS3.and.returnValue(new Promise<void>(res => res()))

      mockClientDocService.destroy.and.returnValue(of(doc))

      component.deleteDocument(doc).then(d => console.log("res", d))
      await fixture.whenStable()

      expect(mockClientDocService.destroy).toHaveBeenCalledTimes(1)
    })
  })
})

I have a feeling it's a jasmine issue but I'm just not sure how to correct the test

CodePudding user response:

First u do not have await before component.deleteDocument call.

Second, deleteDocument is async but it does not wait for this.clientDocService.destroy or this.clientDocService.unlinkSignedDocument.

The way u mix async/await and observables is very confusing. I would advice to select one or another for one method, e.g. with promises/async/await:

 const data = await this.clientDocService.destroy(document.id);
 console.log('doc', data);
 // return {result: 'destroyed', data} -- u generally want to return smth

or with observables

// deleteDocument will return Observable
return from(confirm.onDidDismiss()).pipe(switchMap(() => {
   ...
   return this.clientDocService.destroy(document.id);
}))
  • Related