Тестирование моделей мангустов с NestJS - PullRequest
0 голосов
/ 13 марта 2019

Я использую модуль mongoose от NestJS, поэтому у меня есть схема и интерфейс, и в моем сервисе я использую @InjectModel для внедрения моей модели. Я не понимаю, как я могу издеваться над моделью, чтобы добавить ее в мой сервис.

Мой сервис выглядит так:

    @Injectable()
    export class AuthenticationService {

        constructor(@InjectModel('User') private readonly userModel: Model<User>) {}

        async createUser(dto: CreateUserDto): Promise<User> {
            const model = new this.userModel(dto);
            model.activationToken = this.buildActivationToken();
            return await model.save();
          }
    }

и в моем служебном тесте у меня есть это:

    const mockMongooseTokens = [
      {
        provide: getModelToken('User'),
        useValue: {},
      },
    ];

    beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
          providers: [
            ...mockMongooseTokens,
            AuthenticationService,
          ],
        }).compile();

        service = module.get<AuthenticationService>(AuthenticationService);
      });

Но когда я запустил тест, я получил эту ошибку:

    TypeError: this.userModel is not a constructor

Я также хотел бы, чтобы моя модель выполняла над ней модульные тесты, как показано в этой статье

1 Ответ

0 голосов
/ 18 июня 2019

Ваш вопрос, кажется, не привлек много внимания, так как я столкнулся с той же проблемой, вот решение, которое я реализовал.Надеюсь, что это поможет другим энтузиастам NestJS!

Понимание модели mongoose

Получаемое сообщение об ошибке совершенно очевидно: this.userModel действительно не конструктор, так как вы предоставили пустой объект для useValue,Чтобы гарантировать правильное внедрение, useValue должен быть подклассом mongoose.Model.Мангуста github repo сама дает непротиворечивое объяснение базовой концепции (из строки 63):

 * In Mongoose, the term "Model" refers to subclasses of the `mongoose.Model`
 * class. You should not use the `mongoose.Model` class directly. The
 * [`mongoose.model()`](./api.html#mongoose_Mongoose-model) and
 * [`connection.model()`](./api.html#connection_Connection-model) functions
 * create subclasses of `mongoose.Model` as shown below.

Другими словами, модель мангуста - это класс с несколькими методами, которые пытаютсяподключиться к базе данных.В нашем случае единственным используемым методом Model является save().Mongoose использует синтаксис функции конструктора javascript, тот же синтаксис может использоваться для написания нашего макета.

TL; DR

Макет должен быть функцией конструктора с параметром save().

Написание макета

Сервисный тест заключается в следующем:

  beforeEach(async () => {
    function mockUserModel(dto: any) {
      this.data = dto;
      this.save  = () => {
        return this.data;
      };
    }

    const module = await Test.createTestingModule({
        providers: [
          AuthenticationService,
          {
            provide: getModelToken('User'),
            useValue: mockUserModel,
          },
        ],
      }).compile();

    authenticationService = module.get<AuthenticationService>(AuthenticationService);
  });

Я также провел небольшой рефакторинг, чтобы обернуть все в блоке beforeEach.Реализация save(), которую я выбрал для своих тестов, представляет собой простую функцию идентификации, но вы можете реализовать ее по-разному, в зависимости от того, как вы хотите утверждать возвращаемое значение createUser().

Ограничения этого решения

Одной из проблем этого решения является именно то, что вы утверждаете на возвращаемое значение функции, но не можете утверждать на количество вызовов, так как save() не является jest.fn().Я не смог найти способ использовать module.get для доступа к токену модели за пределами области видимости модуля.Если кто-нибудь найдет способ сделать это, пожалуйста, сообщите мне.

Другая проблема заключается в том, что экземпляр userModel должен быть создан в тестируемом классе.Это проблематично, например, когда вы хотите протестировать findById(), так как модель не создается, но метод вызывается для коллекции.Обходной путь заключается в добавлении ключевого слова new на уровне useValue:

    const module = await Test.createTestingModule({
        providers: [
          AuthenticationService,
          {
            provide: getModelToken('User'),
            useValue: new mockUserModel(),
          },
        ],
      }).compile();

Еще одна вещь ...

Синтаксис return await использовать не следует, так каквыдает ошибку ts-lint (правило: нет возврата-ожидание).См. Связанную проблему github doc .

...