Angular Jasmine Test Timeout

Viewed 675

I am working on an Ionic Angular project and keep getting intermittent failing unit tests for various different tests due to a timeout. Oddly enough, these are just the 'should create' tests that come default with the spec file creation.

Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)

I have read a few related articles/Stack Overflow posts, but none seem to resolve this issue. It's very hard to isolate the issue because sometimes it runs fine, and others it times out. This is happening for a few different tests, but I will provide an example of one of the most basic failing tests.

Component

export class MenuItemCardComponent implements OnInit {

  @Input() menuItem: MenuItem

  constructor(private firebaseService: FirebaseService) { }

  ngOnInit() {
    this.loadImage()
  }

  loadImage() {
    this.firebaseService.loadImage(this.menuItem.id, "menuItems").then(url => {
      this.menuItem.imageUrl = url
    })
  }

}

Service Function (this.storage var is firebase.storage.Storage)

async loadImage(id: string, ref: string) {
    return this.storage.ref(ref).child(`${id}`).getDownloadURL().catch(() => { /* console.log(`Error getting menuItem id: ${error.code}`) */ })
}

Spec File

describe('MenuItemCardComponent', () => {
  let component: MenuItemCardComponent;
  let fixture: ComponentFixture<MenuItemCardComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ MenuItemCardComponent ],
      imports: [IonicModule.forRoot()]
    }).compileComponents();

    fixture = TestBed.createComponent(MenuItemCardComponent);
    component = fixture.componentInstance;
    component.menuItem = {
      title: "Menu Item",
      price: "3.50"
    }
    fixture.detectChanges();
  }));

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
1 Answers

jfd348 is absolutely right. It seems that the times it does time out, the loadImage method call took longer than 5 seconds. To fix it, you have to provide a stub.

Try this:

describe('MenuItemCardComponent', () => {
  let component: MenuItemCardComponent;
  let fixture: ComponentFixture<MenuItemCardComponent>;
  // add this line
  let mockFirebaseService: jasmine.SpyObj<FirebaseService>;

  beforeEach(async(() => {
    // and this one
    // The first string is optional but I provide it for debugging purposes
    // and the second array strings are the public methods you would like to mock
    mockFirebaseService = jasmine.createSpyObj<FirebaseService>('FirebaseService', ['loadImage']);
    TestBed.configureTestingModule({
      declarations: [ MenuItemCardComponent ],
      imports: [IonicModule.forRoot()],
      providers: [
        // provide the mock for the real FirebaseService
        { provide: FirebaseService, useValue: mockFirebaseService }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(MenuItemCardComponent);
    component = fixture.componentInstance;
    component.menuItem = {
      title: "Menu Item",
      price: "3.50"
    }
    // the first fixture.detectChanges calls ngOnInit so we have to provide
    // the results of loadImage before it
    mockFirebaseService.loadImage.and.returnValue(Promise.resolve('xyz'));
    fixture.detectChanges();
  }));

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

With those changes, the test should not have async timeouts.

Related