Skip to content

[Startup MVP recipes #10] Nest.js Unit Testing: Mocking service and the universal mock

Intro and TL; DR

In last article: https://jczhang.com/2022/08/08/startup-mvp-recipes-9-nest-js-typeorm-postgres-unit-testing-service-onlywith-jest-pg-mem/, we had a first glance at how to setup unit test with jest, and inject an in-memory postgres for testing the services.

Now we move back to write unit tests for resolvers, and the idea and philosophy here is too test the resolver interface only: we should isolate the resolver and don’t call the real service. Otherwise if the service fail the resolve will also fail and the resolver unit test are not “unit test” anymore, but more like e2e tests. Therefore, instead, we will mock the service and return the mocked result directly.

A quick takeaway from the approach to be introduced is that it will mock the service from the beginning so the actual test code is even longer than the resolver code itself. And since most of our resolvers are just interfaces, we won’t get every resolver covered with test: it will be a overkill at startups.

References

The Setup

We will use @golevelup/ts-jest to create universal mock with Typescript interfaces

npm i @golevelup/ts-jest

The Code

Here is the code and here are some explanations:

  • We mock the service by creating custom functions that directly return the result
  • The returned result may not be type compatible and we will also use createMock<>() to solve the issue
  • We create the mock service and override it in the original module provider
describe('SmsTemplatesResolver', () => {
  let resolver: SmsTemplatesResolver;
  let service: SmsTemplatesService;

  beforeAll(async () => {
    service = createMock<SmsTemplatesService>({
      createOneBySellerId: async (
        sellerId: number,
        createSmsTemplateInput: CreateSmsTemplateInput,
      ) => {
        return createMock<SmsTemplate>({
          id: 1,
          sellerId,
          ...createSmsTemplateInput,
          cohort: createSmsTemplateInput.cohort ?? 'newsletter',
        });
      },
    });
    const module: TestingModule = await Test.createTestingModule({
      providers: [SmsTemplatesResolver, SmsTemplatesService],
    })
      .overrideProvider(SmsTemplatesService)
      .useValue(service)
      .compile();

    resolver = module.get<SmsTemplatesResolver>(SmsTemplatesResolver);
  });

  it('should be defined', () => {
    expect(resolver).toBeDefined();
  });

  it('should create one', async () => {
    const createSmsTemplateInput: CreateSmsTemplateInput = {
      name: 'Test Template',
      content: 'Test',
      cohort: 'newsletter',
    };
    const result = await resolver.createSmsTemplate(
      766,
      createSmsTemplateInput,
    );
    ObjectTyped.keys(createSmsTemplateInput).forEach((key) => {
      expect(result[key]).toEqual(createSmsTemplateInput[key]);
    });
  });
});

1 thought on “[Startup MVP recipes #10] Nest.js Unit Testing: Mocking service and the universal mock”

  1. Pingback: [Startup MVP recipes #11] Nest.js Run Unit Tests with Github Actions - James Zhang

Leave a Reply

Your email address will not be published. Required fields are marked *