Repository navigation
Description
Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
No
Description
I don't know if this is by design, but the behavior is very different from when working with plain observables or promises.
Consider a simple component
export class TestComponent {
stuff: ResourceRef<string | undefined>;
constructor(service: StuffService) {
this.stuff = resource({
loader: () => firstValueFrom(service.stuff$)
});
}
}
If we write a simple test case that wants to check the state before the first resource value is loaded, the call to whenStable() won't resolve and the test case times out.
const stuff$ = new ReplaySubject<string>(1);
await TestBed.configureTestingModule({
imports: [TestComponent],
providers: [{
provide: StuffService, useValue: { stuff$ }
}]
}).compileComponents();
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
expect(componentIsInStateBeforeStuffIsLoaded()).toBeTrue();
stuff$.next('hello');
fixture.detectChanges();
await fixture.whenStable();
expect(componentIsInStateAfterStuffIsLoaded()).toBeTrue();
The same happens with rxResource.
I would consider a component to be in a stable state while resource is loading the next value. At least it was always the case with asynchronous processes that it's fine to call fixture.whenStable() as many times you want.
This example shows the different behavior when using resource or and async pipe on the source observable directly: https://stackblitz.com/edit/stackblitz-starters-yklwg5m5?file=src%2Fmain.ts
Please provide a link to a minimal reproduction of the bug
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run ng version)
Angular CLI: 19.2.4
Node: 22.13.0
Package Manager: npm 10.9.2
OS: win32 x64
Angular: 19.2.3
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1902.4
@angular-devkit/build-angular 19.2.4
@angular-devkit/core 19.2.4
@angular-devkit/schematics 19.2.4
@angular/cli 19.2.4
@schematics/angular 19.2.4
rxjs 7.8.2
typescript 5.7.3
zone.js 0.15.0
Anything else?
No response
Activity
JeanMeche commentedon Apr 4, 2025
This is by design, your resource is awaiting its value and by nature keeps the app unstable.
hansmaad commentedon Apr 4, 2025
Ok, I would consider
@if (res.isLoading()) { ... }a stable component state that last until it switches to false, but I don't have to question that design decision here. I noticed that I can use fakeAsync and tick to make my expectations during that "unstable" state. Is this the recommended solution?atscott commentedon Apr 4, 2025
This would mean apps would serialize with loaders rather than having all their resource data in the loaded state.
You can use a mock clock or wait real time. Something like waitFor is particularly useful and allows you to write unit tests in the same style as you would an e2e test (you could even use the same test body between environments).
hansmaad commentedon Apr 4, 2025
We used to have test suites like this a lot when testing components that do async stuff
It worked well with
await fixture.whenStable()regardless of which async/reactive style you took, Promises, Observables, Signals, async pipe. But if you wrap this in a resource or rxResource the behaviour ofwhenStableis different. I, as a developer, found this very suprising and I thought it's probably just buggy in the current state.I'm on Angular(js) since v1.2, and to be honest, I've never seen a whenStable() promise that did not resolve, or something that holds a component unstable forever... So that one was just surprising ;)
marwesfur commentedon May 28, 2025
The testing scenario @hansmaad describes is very typical for our applications as well.
To make things worse, CDK ComponentHarnesses automatically call
whenStablewhen auto change detection is not disabled (see https://github.com/angular/components/blob/main/src/cdk/testing/testbed/testbed-harness-environment.ts#L84 and https://github.com/angular/components/blob/main/src/cdk/testing/testbed/testbed-harness-environment.ts#L174) which then always causes tests to timeout when resources in loading state are involved.@atscott I can't fully agree to that one:
To me it seems typical that an application loads from several independent data sources in parallel (using the resource api) to improve perceived performance -- yet some actions might depend on the loading state of more than one such data (re)source. Testing this in a straightfoward manner is currently impossible thanks to that (imo) questionable design decision.
I might have misunderstood your argument, though...
@hansmaad
respect for your humbleness 😄
atscott commentedon May 28, 2025
This seems separate from the SSR use-case. Loading data in parallel has nothing to do the server waiting for stabilization before serializing.
IMO, the decision that is in question is the one to wait for stability in the TestBed harnesses. The opt out you have is wrapping some code in
manualChangeDetectionwhere it would have been waiting for stability.marwesfur commentedon May 28, 2025
Ok for server side rendering, yes, I can follow why it makes sense to treat an application with loading resources as unstable. I was fully focused on client side rendering (as was the OP I assume), that's why I misunderstood your point.
And I hope that you can at least also understand why this design causes inconvenience for testing in client side rendering scenarios, in particular because
Bottom line: I'll keep using the
manualChangeDetectionworkaround. Thanks for you explanations.jermowery commentedon Aug 17, 2025
This also caused problems for me both inside and outside of g3. It is just unclear that the pending resource state would cause this behavior and it is unclear that the harnesses are waiting for stability.
I am glad that we have a workaround, but it would be good to signal (pun intended) this more clearly in the resource docs. The test scenarios described here are extremely common. I have personally done and reviewed code where people abandoned using
resourcebecause tests like the ones described here started failing and it was very unclear why and was hand waved as "resource is experimental and not ready to use"wawyed commentedon Nov 3, 2025
Is there going to be any updates to handle this better? I'm concerned about using
rxResourceif we can't integrate properly with harnessesatscott commentedon Nov 3, 2025
For harnesses, you can use
manualChangeDetectionto prevent them from waiting for stability. There are no planned changes in the framework to make tests work differently from production with respect to stability.wawyed commentedon Nov 3, 2025
Would it be possible to add a way to disable it for the whole loader? that would make it a little bit easier than having to manually add it to every it/bEach that uses a harness
JeanMeche commentedon Jun 8, 2026
I'll closing this as a dupe of #68808 where comments mention how to better test resources.
angular-automatic-lock-bot commentedon Jul 9, 2026
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.