Mocks

External dependencies can be simulated by simple representations.

Inside the src folder, create the file testing -> util -> testing.util. Here, we can create a type to represent a MockRepository, and a function to instantiate one. Let's then get to it.

Fortunately, with the library we installed earlier, this process will become much simpler. We can use the MockProxy type and pass the Repository, in order to generate the type for a MockRepository. The entity does not matter in this case, so we can use any.

export type MockRepository = MockProxy<Repository<any>>;

We can then create a function that will generate a MockRepository. A similar process will be performed, passing the Repository to the mock() function as a type parameter.

export const createMockRepository = () => mock<Repository<any>>();

The result will be the following: we'll get a repository that has all the methods of the original class, but all of them have no logic. And, for each scenario, a specific behavior can be applied.

Going back to the test file, we shall perform the following steps:

  • Create a variable for the repository below the service

let repository: MockRepository;
  • Call the function to create a MockRepository

useValue: createMockRepository(),
  • Obtain its reference from the module

repository = module.get<MockRepository>(getRepositoryToken(User));

Back in the test for the findOne() method, if we then mock the method that would be called in the repository (and do it before the service call, of course), the test will pass!

repository.findOneByOrFail.mockResolvedValueOnce(expectedUser);

Some considerations:

  • The method mockReturnValue() makes the chained method return the value

  • By using Resolved, a resolved promise is returned

  • And by using Once, after this call the logic is erased again

Before proceeding to the other tests, let's just create three auxiliary functions at the bottom of the file. The faker library will be used to automatically generate fake data. But we need to import it manually.

import { faker } from '@faker-js/faker';

They will be for creating:

  • A fake createUserDto

const genCreateDto = (): CreateUserDto => ({
  name: faker.person.firstName(),
  email: faker.internet.email(),
  phone: faker.phone.number(),
  password: faker.internet.password(),
});
  • A fake updateUserDto

const genUpdateDto = (): UpdateUserDto => ({
  name: faker.person.firstName(),
});
  • A fake user

const genUser = (id: number, createDto?: CreateUserDto) =>
  ({
    id,
    ...(createDto ?? genCreateDto()),
  } as User);

The last function may or may not receive a createUserDto

  • If it does, the user will receive its data

  • If not, the data will be random

With the help of these, we can more easily write our tests. Let's then just use the last one in the test to generate the expectedUser...

const expectedUser = genUser(id);

...and then proceed to the remaining tests.

Last updated