Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signals : effect() is not triggered for unit tests #50466

Closed
MGX-CODING opened this issue May 25, 2023 · 11 comments
Closed

Signals : effect() is not triggered for unit tests #50466

MGX-CODING opened this issue May 25, 2023 · 11 comments
Labels
area: core Issues related to the framework runtime area: testing Issues related to Angular testing features, such as TestBed core: reactivity Work related to fine-grained reactivity in the core framework cross-cutting: signals
Milestone

Comments

@MGX-CODING
Copy link

Which @angular/* package(s) are the source of the bug?

core

Is this a regression?

No

Description

When unit testing an effect, it seems that it cannot be triggered. I would expect my effect to trigger so that I can test its content.

Here is a ZIP file of a simple project to test it : unzip it, run npm install, then npx ng test.

Main files are app.service.ts and app.service.spec.ts, code is fairly simple you'll see.

test.zip

Please provide a link to a minimal reproduction of the bug

Please see above

Please provide the exception or error you saw

No exception, just no effect trigger

Please provide the environment you discovered this bug in (run ng version)

_                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 16.0.2
Node: 18.10.0
Package Manager: npm 8.19.2
OS: linux x64

Angular: 16.0.3
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1600.2
@angular-devkit/build-angular   16.0.2
@angular-devkit/core            16.0.2
@angular-devkit/schematics      16.0.2
@angular/cli                    16.0.2
@schematics/angular             16.0.2
rxjs                            7.8.1
typescript                      5.0.4

Anything else?

No response

@jessicajaniuk jessicajaniuk added area: core Issues related to the framework runtime cross-cutting: signals labels May 25, 2023
@ngbot ngbot bot modified the milestone: needsTriage May 25, 2023
@JeanMeche
Copy link
Member

JeanMeche commented May 25, 2023

Hi !
Thank you for providing a repro !

Effects are currently hooked onto the change detection system, if there is no change detection, they won't be fired.

The same test on a component will be fine :

@Component({ template: '' })
export class Comp {
  sig = signal(0);

  constructor() {
    effect(() => {
      const value = this.sig();
      console.log(value);
    });
  }

  trigger() {
    this.sig.set(Math.random());
  }
}

describe('Comp', () => {
  it('Should trigger', () => {
    console.log = jasmine.createSpy('console').and.callThrough();
    const fixture = TestBed.createComponent(Comp);
    fixture.componentRef.instance.trigger();
    fixture.detectChanges()
    expect(console.log).toHaveBeenCalled();
  });
});

@Maryannah
Copy link

Maryannah commented May 25, 2023

Hey, thank you for the information !

Does that mean effects can't be tested on services ? I'm not aware of change detection for those ... Would that also imply that services should avoid using effects ? It's relatively new to me, so I'll take any information I can find !

Edit : sorry this is the same person, just a different account on my phone

@JeanMeche
Copy link
Member

Of course they can be tested when there is a component in the flow of the test :

class Service {
  sig = signal(0);

  constructor() {
    effect(() => {
      console.log(this.sig());
    });
  }
}

@Component({ template: '' })
export class Comp {
  sig = signal(0);

  constructor() {
    effect(() => {
      const value = this.sig();
      console.log(value);
    });
  }

  trigger() {
    this.sig.set(Math.random());
  }
}

describe('Comp', () => {
  it('Should call console.log twice', () => {
    TestBed.configureTestingModule({ providers: [Service] });
    const a = TestBed.inject(Service);
    console.log = jasmine.createSpy('console').and.callThrough();
    const fixture = TestBed.createComponent(Comp);
    fixture.componentRef.instance.trigger();
    fixture.detectChanges();
    expect(console.log).toHaveBeenCalledTimes(2);
  });
});

In this use case, both effects are called because change detection happened (because of the component, as you assumed correctly).

@Maryannah
Copy link

So I would have to create a stubbed component for every service with effects that I want to test ?

To be honest I usually do unit testing without the testbed, by manually creating instances of my elements, I tried to get back to the testbed for the signals, but if I also have to add this step too, it seems a bit overkill to me ...

@JeanMeche
Copy link
Member

Signals don't really make sense without components. They exist to bring fine grained reactivity to components, thus effect doesn't really make sens outside of Change Detection.

If you're looking for traditionnal reactivity, RxJs is still your best horse !

@Maryannah
Copy link

In my (particular) case, the effect is listening on the Auth service to get the user, then makes an http request to update another state management service, that is used by components. I thought it would be a cool way of using signals to discover them !

But thank you for your input on that, I will indeed stay on rxjs and keep signals for my components. You were very helpful, thank you !

@lacolaco
Copy link
Contributor

lacolaco commented Jun 2, 2023

I am facing the same problem, but according to the RFC, Signals is never intended to be used only in components.
#49683
If we are relying on change detection in components at this point, that seems to be an issue that should be fixed in the future.

From reading the effect implementation, I believe that if I can get an instance of EffectManager from TestBed, I should be able to trigger the effect with the flush() method.

https://github.com/angular/angular/blob/main/packages/core/src/render3/reactivity/effect.ts#L33

I think it would be worth looking into making EffectManager a public API, or adding a testing utility API that calls EffectManager.flush().

@yjaaidi
Copy link
Contributor

yjaaidi commented Jun 2, 2023

Totally agreed @lacolaco!
That said, it would be nice if TestBed could provide some syntactic sugar above it anyway (e.g. flushEffects()).
In that case, why not just expose the syntactic sugar instead of the whole EffectManager which is more of an implementation detail?

Meanwhile, if that can help someone here, here is how we dealt with this for @jscutlery/rx-computed:

https://github.com/jscutlery/devkit/blob/43924070eb8433f56ff9a2e65a24bf48b4a5122e/packages/rx-computed/src/lib/rx-computed.spec.ts#L133

@JeanMeche
Copy link
Member

With #51049 we would have a work arround by triggering a Microtask like with await Promise.resolved().

@pkozlowski-opensource pkozlowski-opensource added area: testing Issues related to Angular testing features, such as TestBed core: reactivity Work related to fine-grained reactivity in the core framework labels Jul 17, 2023
@pkozlowski-opensource
Copy link
Member

#51049 landed and it has a new flushEffects() method on TestBed (plus TestBed will flush effects as part of change detection).

@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Oct 14, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: core Issues related to the framework runtime area: testing Issues related to Angular testing features, such as TestBed core: reactivity Work related to fine-grained reactivity in the core framework cross-cutting: signals
Projects
None yet
Development

No branches or pull requests

7 participants