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

Clarify how to test for errors in effects #55753

Open
DmitryEfimenko opened this issue May 10, 2024 · 2 comments
Open

Clarify how to test for errors in effects #55753

DmitryEfimenko opened this issue May 10, 2024 · 2 comments
Labels
area: core Issues related to the framework runtime area: docs Related to the documentation 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

@DmitryEfimenko
Copy link

Describe the problem that you experienced

Given a component that has an effect that throws an error, it's not clear what is the correct way to write a unit test to test for that error. Please see the code below. This is a standard way to test that something throwing an error. However, this unit test does not pass saying that exception was not thrown. Notably, the expected thrown error is visible in the terminal.

class MyComponent {
  name = input.required<string>();
  
  private validateNameEffect = effect(() => {
    if (this.name() !== 'Tom') {
      throw new Error('Not Tom')
    }
  });
}

it('should throw error', () => {
  expect(() => {
    const fixture = TestBed.configureTestingModule({
      imports: [MyComponent],
    }).createComponent(MyComponent);
	
    fixture.detectChanges();
	
    TestBed.flushEffects();
  }).toThrow()
});

I figured out a way to make test pass using custom ErrorHandler. However, that seems very hacky:

class TestErrorHandler extends ErrorHandler {
  caughtError: any;
  
  override handleError(error: any) {
    this.caughtError = error;
    // not calling original error handling to avoid
    // terminal being polluted
    // super.handleError(error);
  }
}

it('should throw error', () => {
  const fixture = TestBed.configureTestingModule({
    imports: [MyComponent],
    providers: [
      { provide: ErrorHandler, useClass: TestErrorHandler },
    ],
  }).createComponent(MyComponent);

  const errorHandler = TestBed.inject(ErrorHandler) as unknown as TestErrorHandler;
  
  expect(errorHandler.caughtError).toBeDefined();
});

Enter the URL of the topic with the problem

https://twitter.com/dmitryaefimenko/status/1788714569420173689

Describe what you were looking for in the documentation

Clear instructions on how to test for errors in effects

Describe the actions that led you to experience the problem

No response

Describe what you want to experience that would fix the problem

No response

Add a screenshot if that helps illustrate the problem

No response

If this problem caused an exception or error, please paste it here

No response

If the problem is browser-specific, please specify the device, OS, browser, and version

No response

Provide any additional information here in as much as detail as you can

No response

@JeanMeche JeanMeche added core: reactivity Work related to fine-grained reactivity in the core framework cross-cutting: signals area: core Issues related to the framework runtime labels May 10, 2024
@ngbot ngbot bot added this to the needsTriage milestone May 10, 2024
@JeanMeche
Copy link
Member

JeanMeche commented May 10, 2024

Context:

try {
this.effectFn(onCleanup);
} catch (err) {
// Inject the `ErrorHandler` here in order to avoid circular DI error
// if the effect is used inside of a custom `ErrorHandler`.
const errorHandler = this.injector.get(ErrorHandler, null, {optional: true});
errorHandler?.handleError(err);
}

Effects do log their error to the error handler, so you're on correct path.

You could just spy the handleError method for example:

test('should throw error', () => {
  const fixture = TestBed.configureTestingModule({
    imports: [MyComponent],
  }).createComponent(MyComponent);

  const errorHandler = TestBed.inject(ErrorHandler);
  const spy = vi.spyOn(errorHandler, 'handleError');
  fixture.detectChanges();
  expect(spy).toBeCalledWith(new Error('foobar'));
});

https://stackblitz.com/edit/angular-vitest-starter-v18-ryiakv

@DmitryEfimenko
Copy link
Author

sounds good, thanks for the clarification. Do you think this should be mentioned somewhere in the docs?

@JeanMeche JeanMeche added area: docs Related to the documentation area: testing Issues related to Angular testing features, such as TestBed labels May 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: core Issues related to the framework runtime area: docs Related to the documentation 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

2 participants