Home > other >  Angular test fails despite console logging correct value
Angular test fails despite console logging correct value

Time:11-18

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')
    }))
  })
  • Related