Should Angular components and services always avoid using setInterval (or rxjs timer)?

168 views Asked by At

I have some Components and Services that perform operations periodically in the background -- refreshing data, updating the displayed date/time, etc. I originally wrote these features using setInterval. Now, I'm going back to add unit tests. I spent way too long trying to figure out why my tests weren't working -- turns out that fixture.whenStable() will never resolve if the component under test (or any of its dependencies!) create an interval. I tried switching from setInterval to rxjs interval / timer, but those have the same problem.

The advice given on this older question is to put actual setInterval calls inside a dedicated "interval service", then mock out the service with something tick-able when writing tests. I don't love the idea of changing my (working!) code to accommodate testing, if I can help it, but I think I really need whenStable to work. Among other things, the Angular Material TestbedHarnessEnvironment uses it under the hood, so as far as I can tell, any component that includes an interval can't use Material harnesses.

My question is: as of late 2020, do I have any better options? Is there a way to write waitForAsync-style tests for Components or Services that include an actual interval? Or is there a better pattern I can use for my Component design, maybe some interval replacement that's integrated with existing Zone.js testing patches?

1

There are 1 answers

1
Reuben Sivan On

I had a similar (not identical) problem, which I solved as follows: in the test code, right after invoking fixture.detectChanges(), I stopped my timers (by unsubscribing). The only change in the tested code is to expose the unsubscribe code with a public function, for example:

stopTimers() {
  this.intervalSubs?.unsubscribe();
}

Then in your test code:

component = fixture.componentInstance;
fixture.detectChanges();
component.stopTimers();