rugk
rugk

Reputation: 5483

Jasmine test times out with "Async callback was not invoked within 5000ms" altghough no async function is used in my Angular project tests

Here is the source code:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { IonicModule } from '@ionic/angular';

import { HomePage } from './home.page';
import { LevelGridComponent } from '../level/level-grid/level-grid.component';
import { SidebarComponent } from '../sidebar/sidebar.component';
import { LevelStatusSidebarComponent } from '../level/level-status-sidebar/level-status-sidebar.component';

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

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

    fixture = TestBed.createComponent(HomePage);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }));

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

Now the test opens the browser, renders the component and then does nothing, for 5 seconds, and (thus) seems to time out:

HomePage should create FAILED Error: Timeout - Async callback was not invoked within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL) in node_modules/jasmine-core/lib/jasmine-core/jasmine.js (line 5494)

Things that happened before

Previously, I had the usual Angular "this module is not defined" error, i.e.:

'app-level-status-sidebar' is not a known element:
1. If 'app-level-status-sidebar' is an Angular component, then verify that it is part of this module.
2. If 'app-level-status-sidebar' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("

To fix this, I've added the real components to "declarations" as you can see above.

I think this was because in the HTML I've used other components, i.e. the ones I've added above.


As the test is actually very simple, I do not expect any issues here, and it also seems to run (I've added console.log statements and they were executed in it('should create'.) As the test function does not actually initiate any async function and is 100% synchronous, I did not expect anything to fail here.

For testing I've also added an explicit done function call to the it test function and call it at the end, but that does not change anything.


Edit: I think I am getting behind the problem. So it seems:

Here is the source code for the timer:

public timeLeft = 5;
// […]

private startTimer() {
    const counter$ = interval(1000);

    const takeFourNumbers = counter$.pipe(
      take(this.timeLeft),
      takeUntil(this.ngUnsubscribe)
    );
    takeFourNumbers.subscribe(x => {
      this.timeLeft--;
      if (this.timeLeft === 0) {
        this.levelTimeout.emit();
      }
    });
  }

This still does not resolve the first problem with defining the components inside of the tests, because the error still happens, so I still need to define them. And I still don't get the technical details why a random unresolved Observable inside of a component's dependency chain results in that effect that the component somehow "times out" in the test. (even though it is just checked to be "truthy", i.e. to exist)

Upvotes: 1

Views: 729

Answers (2)

Ivan Kukushkin
Ivan Kukushkin

Reputation: 161

I had the same issue. Remove the "async" (or "waitForAsync") from your "beforeEach" function. You don't use any "await" there:

beforeEach(() => {
  TestBed.configureTestingModule({
    declarations: [
      HomePage,
      LevelGridComponent,
      SidebarComponent,
      LevelStatusSidebarComponent
    ],
    imports: [IonicModule.forRoot()]
  }).compileComponents();

  fixture = TestBed.createComponent(HomePage);
  component = fixture.componentInstance;
  fixture.detectChanges();
});

Upvotes: 0

Kyle Anderson
Kyle Anderson

Reputation: 754

If you're unit testing your home page you shouldn't be adding child components to your declarations. Instead you could just create a Stubbed version of that component. You can just put something like the below at the bottom of your spec file and add StubLeveltatusSidebarComponent to your declarations instead (you may need to stub out inputs as well).

@Component({ selector: 'app-level-status-sidebar', template: '' })

class StubLevelStatusSidebarComponent{}

Alternatively you can try adding the below to your configureTestingModule object and it should ignore missing children errors and continue on with the tests.

schemas: [NO_ERRORS_SCHEMA]

Upvotes: 1

Related Questions