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

Docs: Use async method when testing effects without TestBed #2449

Open
MrCube42 opened this issue Mar 25, 2020 · 2 comments
Open

Docs: Use async method when testing effects without TestBed #2449

MrCube42 opened this issue Mar 25, 2020 · 2 comments

Comments

@MrCube42
Copy link

@MrCube42 MrCube42 commented Mar 25, 2020

Background

I decided to test my effects without using the Angular TestBed because this should be faster then using the entire TestBed when I only want to test the effect in isolation (https://ngrx.io/guide/effects/testing#setup-without-testbed). I am using Jest as test runner.

Repro-Steps

Consider this simplified, generic approach I used.

  it('should get data on success', () => {
    // Arrange.
    const sampleData = { ... }
    const serviceMock = {
      getMyData: jest.fn().mockReturnValue(of(sampleData)),
    };

    const actions$ = new Actions(
      of(loadData({ id: 42 })),
    );

    const effects = new MyEffects(actions$, serviceMock);

    // Act.
    effects.loadData$.subscribe(action => {
      // Assert.
      expect(action).toEqual(
        loadDataSuccess({ data: sampleData }),
      );
    });
  }));

What's the problem?

Unfortunately the test is green, however the coverage showed me that it is not executed properly. This is due to two problems:

  • The jest.fn() call does not execute anything and is wrong. This is an error from my side.
  • However, in this case we want to see that an error occurs and the test should fail.

How to fix it? What do change?

If the test itself was run inside of async, then we would know that actually the service's getMyData has never been called because of the wrong implementation of the mock.

The docs may propose to wrap the async test into the async method call like this:

  it('should get data on success', async(() => { ...

Side Note

The test itself can than be fixed by don't mocking out the call by jest, but letting it go through.

Instead of using this:

getMyData: jest.fn().mockReturnValue(of(sampleData)),

We must use this:

getMyData: jest.fn(() => of(sampleData)),

Other information:

I would be willing to submit a PR for the docs ❤️

[x] Yes (Assistance is provided if you need help submitting a pull request)
[ ] No

@brandonroberts
Copy link
Member

@brandonroberts brandonroberts commented Apr 10, 2020

@MrCube42 what are you proposing in terms of docs, as we don't have any Jest-specific examples in the testing guide.

@MrCube42
Copy link
Author

@MrCube42 MrCube42 commented Apr 11, 2020

To summarize, my proposal is: Adding the async() wrapper from @angular/core/testing to the test example my.effects.spec.ts in the docs because of the asynchronous nature of this effects test.


I tried to reproduce the scenario with standard Jasmine/Karma setup of Angular.
Consider the following test:

it('should get data on success', () => {
  // Arrange.
  const sampleData = ['foo', 'bar'];
  spyOn(serviceMock, 'getMyData'); // <- This is en error. Missed to provide a mock implementation

  const actions$ = new Actions(of(loadData({ id: 42 })));

  const effects = new DataEffects(actions$, serviceMock);

  // Act.
  effects.loadData$.subscribe((action) => {
    // Assert.
    expect(action).toEqual(loadDataSuccess({ data: sampleData }));
  });
});

In this case, the test is green and the error due to the missing mock implementation of the getMyData function is shown in the console:
image

Only looking at the console or the code coverage reveals that there is an error in the test itself.

However, if we would wrap the test function in an asynchronous test zone, we would receive the error during the test run and the test would actually fail:

it('should get data on success', async(() => { ... }))
image

In the end, this would be more fail safe. I am not 100% sure, but I suppose the test would formally be more correct if it is wrapped into the async zone because of the existing subscription.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.