docs.nestjs.com icon indicating copy to clipboard operation
docs.nestjs.com copied to clipboard

suggestion: add recipe for @automock/jest for automocking capabilities

Open omermorad opened this issue 3 years ago • 3 comments

I'm submitting a:

  • [ ] Regression
  • [ ] Bug report
  • [x] Feature request
  • [x] Documentation issue or request (new chapter/page)
  • [ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Hi,

In this pull request (https://github.com/nestjs/nest/pull/6277) there is a discussion regarding auto mocking capabilities using the testing module which has been created by @jmcdo29. I added a comment there back at the time which describes another solution for auto mocking capabilities for (solitary) unit tests.

Before I create a pull request to NestJS docs, I thought it worth presenting the package here and having a discussion about it.

The package is called "@automock/jest" (here is a link). It suggests a simple mechanism (using reflection) for auto mocking the class constructor's dependencies (without loading the whole module). here is a short example for the sake of the discussion:

Consider the following provider/class:

@Injectable()
export class SomeService {
  public constructor(
    private readonly logger: Logger,
    private readonly catsService: CatsService,
    private readonly userService: UserService,
    private readonly featureFlagService: FeatureFlagService,
  ) {}
  
  public async doSomethingNice() {
    if (this.featureFlagService.isFeatureOn()) {
      const users = await this.userService.getUsers('https://example.com/json.json');
      this.logger.log(users);

      return users;
    }
  }
}

And now look the spec file:

import { Spec } from '@automock/jest';
import Mocked = jest.Mocked;

describe('SomeService Unit Test', () => {
  let someService: MainService;

  let logger: Mocked<Logger>;
  let userService: Mocked<UserService>;

  const USERS_DATA = [{ name: 'user', email: '[email protected]' }];

  beforeAll(() => {
    const { unit, unitRef } = Spec.create(MainService)
      .mock(FeatureFlagService)
      .using({ isFeatureOn: () => Promise.resolve(true) })
      .compile();

    someService = unit;
    userService = unitRef.get(UserService);
  });

  describe('When something happens', () => {
    beforeAll(() => (userService.getUsers.mockResolvedValueOnce(USERS_DATA));
    
    test('then check something', async () => {
      await service.doSomethingNice();
      expect(logger.log).toHaveBeenCalledWith(USERS_DATA);
    });
  });
});

It also works with custom tokens (when you have an interface and not a concrete class). As said, because the dependencies are being mocked (actually stubbed) automatically, and everything is replaced - there is no need to load the whole module, thus, the spec runs really (really) fast and it's very easy to write.

I would love to hear what are you guys think (CC: @jmcdo29)

Thanks!

omermorad avatar Apr 15 '22 14:04 omermorad

@jmcdo29 do you want to create a PR?

omermorad avatar Apr 27 '22 15:04 omermorad

I think this looks like a really cool package. I think we could at least open a PR for a new Recipe showing it off. If nothing else I'd love to see a PR against my testing-nestjs repo to show it off to others as well, similarly to how I've integrated @golevelup/ts-jest. Really love seeing all this tooling showing up!

jmcdo29 avatar Apr 27 '22 15:04 jmcdo29

Thank you @jmcdo29! Really trying to improve Nest and TypeScript ecosystem so we can all enjoy some great tools :) I will create a PR at testing-nestjs repository and we'll continue from there!

omermorad avatar Apr 28 '22 13:04 omermorad

This issues we could close it since there is a PR here, right ?

Tony133 avatar Jan 27 '23 20:01 Tony133