Search code examples
unit-testingjestjsnestjsautomapperts-jest

Jest unit test automapper nestjs


jest.mock<CreateProductCommandHandler>(
  './commands/create.product.command.handler',
);
jest.mock<ProductRepository>('./infrastructure/product.repository');

describe('ProductController', () => {
  let productController: ProductController;
  let createProductCommandHandler: CreateProductCommandHandler;
  let productRepository: ProductRepository;
  let eventPublisher: EventPublisher;
  let commandBus: CommandBus;
  let queryBus: QueryBus;
  let mapper: Mapper;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [AutomapperModule],
      controllers: [ProductController],
      providers: [
        CommandBus,
        QueryBus,
        AutomapperModule,
        CreateProductCommandHandler,
        {
          provide: ProductRepository,
          useValue: productRepository,
        },
        {
          provide: EventPublisher,
          useValue: eventPublisher
        },
        ProductRepository,
      ],
    }).compile();

    //mapper = moduleRef.get<Mapper>(typeof(Mapper));
    productController = moduleRef.get<ProductController>(ProductController);
    productRepository = moduleRef.get<ProductRepository>(ProductRepository);
    commandBus = moduleRef.get<CommandBus>(CommandBus);
    queryBus = moduleRef.get<QueryBus>(QueryBus);

    createProductCommandHandler = moduleRef.get<CreateProductCommandHandler>(
      CreateProductCommandHandler,
    );
    jest.clearAllMocks();
  });

Here is the controller class

@Controller()
export class ProductController {
  constructor(
    private readonly commandBus: CommandBus,
    private readonly queryBus: QueryBus,
    @InjectMapper() private readonly mapper: Mapper,
  ) {}

Here is the Automapper profile

@Injectable()
export class ProductProfile extends AutomapperProfile {
  constructor(@InjectMapper() mapper: Mapper) {
    super(mapper);
  }

  get profile(): MappingProfile {
    return (mapper: Mapper) => {
      createMap(mapper, ProductModel, CreateProductCommand);
      createMap(mapper, CreateProductCommand, ProductDomain);

      createMap(mapper, UpdateProductModel, UpdateProductCommand);
      createMap(mapper, UpdateProductCommand, ProductDomain);

      createMap(mapper, ProductModel, ProductDomain);
      createMap(mapper, ProductDomain, ProductModel);

      createMap(mapper, ProductDomain, ProductDocument);
      createMap(mapper, ProductDocument, ProductDomain);
      createMap(mapper, ProductDocument, ProductModel);
    };
  }

  protected get mappingConfigurations(): MappingConfiguration[] {
    return [
      extend(FzAbstractModel, FzAbstractCommand),

      extend(FzAbstractModel, FzAbstractDomain),
      extend(FzAbstractDomain, FzAbstractModel),

      extend(FzAbstractDomain, FzAbstractDocument),
      extend(FzAbstractDocument, FzAbstractDomain),
    ];
  }
}

I am using @nestjs/cqrs and a service class with automapper injected in controllers and service and trying to run unit test.

Mapper is actually an interface from @automapper/core.

I need a help on mocking and resolving the dependency of automapper. Because when I run the test it throws an error

Nest can't resolve dependencies of the ProductController (CommandBus, QueryBus, ?). Please make sure that the argument automapper:nestjs:default at index [2] is available in the RootTestModule context.


Solution

  • Finally with the help of @MicaelLevi, I implemented this and the mock works fine for automapper. The Automapper Profiles must also be passed in the providers section.

      let mapper: Mapper;
    
      beforeEach(async () => {
        const moduleRef = await Test.createTestingModule({
          imports: [AutomapperModule],
          controllers: [ProductController],
          providers: [
            CommandBus,
            QueryBus,
            {
              provide: getMapperToken(),
              useValue: createMapper({
                strategyInitializer: classes(),
              }),
            },
            BaseProfile,
            ProductProfile,
            CreateProductCommandHandler,
            {
              provide: ProductRepository,
              useValue: productRepository,
            },
            {
              provide: EventPublisher,
              useValue: eventPublisher,
            },
            ProductRepository,
          ],
        }).compile();
    
        mapper = moduleRef.get<Mapper>(getMapperToken());