angular icon indicating copy to clipboard operation
angular copied to clipboard

Clarify how to test for errors in effects

Open DmitryEfimenko opened this issue 9 months ago • 2 comments

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

DmitryEfimenko avatar May 10 '24 19:05 DmitryEfimenko

Context:

https://github.com/angular/angular/blob/28fb385eec03f64b624a9b67d6d8147b6872a939/packages/core/src/render3/reactivity/effect.ts#L175-L182

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

JeanMeche avatar May 10 '24 20:05 JeanMeche

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

DmitryEfimenko avatar May 10 '24 21:05 DmitryEfimenko