angular / angular/components

bug(TestbedHarnessEnvironment): Impossible to use if component under test uses intervals

Open
#20,785 5 comments 0 reactions 0 assignees View on GitHub
area: cdk/testing P3
Dominant language
TypeScript
Stars
25k
Forks
6.8k
Avg merge
1d 8h
Merged PRs (30d)
91

Description

Use StackBlitz to reproduce your issue: **ADDED** (thanks @annieyw!), [example here](https://stackblitz.com/edit/material-testbedharness-interval?file=src/app/button-harness-example.spec.ts)

Steps to reproduce:
1. Follow [the instructions for using Harnesses](https://material.angular.io/guide/using-component-harnesses)
2. In the component under test, use `setInterval`, or the rxjs `interval(n)` or `timer(0, n)` observables
3. Your test will stall indefinitely at e.g. `await loader.getAllHarnesses(MatButtonHarness);` (from the docs linked in step 1)

#### Expected Behavior

`getAllHarnesses` completes once the component has finished initializing and had a chance to create instances of child components.

#### Actual Behavior

The call to `getAllHarnesses` never resolves, and the test times out.

#### Environment

- Angular: 10.1.2
- CDK/Material: 10.2.1
- Browser(s): N/A
- Operating System (e.g. Windows, macOS, Ubuntu): N/A (Win10)

----

As I understand it, this is because under the hood, `TestbedHarnessEnvironment` is using `ComponentFixture#whenStable`. From what I can tell, [the official stance from Angular / Zone](https://github.com/angular/angular/issues/10127) is that you should use `fakeAsync` plus `tick`, and call `discardPeriodicTasks` to ignore the (expected) pending intervals, instead of using `waitForAsync` / `whenStable`.

This [very old StackOverflow question](https://stackoverflow.com/questions/38697656/testing-angular2-components-that-use-setinterval-or-settimeout) suggests that one should never have `setInterval` in a component or service, instead using an injected service to provide periodic actions, so it can be mocked out during testing, but I've never seen anything "official" from Angular to say that intervals are unsupported.

If it's not permissible to use intervals in a component, this should be documented somewhere, with suggestions of alternative design patterns to use that are compatible with Material's test utilities. If it is permissible, `TestbedHarnessEnvironment` should be updated to include another way of determining when the owning `ComponentFixture` is "ready", or I guess kick this upstream to the Zone folks so they can make `whenStable` work with intervals.

Contributor guide

Open the contributing guide

Research direction

Start with the StackBlitz reproduction and the TestbedHarnessEnvironment entry point, focusing on its use of ComponentFixture#whenStable. Compare the behavior with the linked Angular and Zone guidance for fakeAsync, tick, and discardPeriodicTasks. Done means the interval case has an agreed supported behavior, either a working harness path or documentation explaining the limitation and compatible testing pattern.

Written by the indexing model from the issue text.

Assessment

Tech stack
angular, typescript
Domain
testing
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.