Search code examples
nestjsnestjs-i18n

Unit testing when using nestjs-i18n I18nContext - Cannot read properties of undefined (reading 'lang')


A quick question, as I've looked about the web and haven't found anything so far.

I have a message service which makes use of the I18nService as a dependency and also I18nContext.current().lang in order to set the desired language.

Inside my message service I have created a simple error message method which returns a translated string based on status code:

const currentLanguage = I18nContext.current().lang; <-- issue is here, doesn't know what it is when testing with jest

switch (statusCode) {
    case HttpStatus.AMBIGUOUS:
        return this._i18n.translate('exceptions.AMBIGUOUS', {
            lang: currentLanguage,
        });

This all works fine and translations return when I manually test the currentLanguage constant.

The issue I have is unit testing the service.

I have created a basic mocki18nservice, but when the method is triggered in the test, I consistently get:

TypeError: Cannot read properties of undefined (reading 'lang')

Has anyone found a way of mocking I18nContext, or bringing it in successfully in their TestingModule yet? I have looked over multiple projects using the package, but there isn't any test files on show to see how this has been done in the past.

when testing, a very simple test:

describe('Testing getErrorMessage()', () => {
    it('should return exceptions.AMBIGUOUS with status code 300', () => {
        const spyService = jest.spyOn(service['_i18n'], 'translate');
        service.getErrorMessage(300);
        expect(spyService).toHaveBeenCalledWith('exceptions.AMBIGUOUS', {
            lang: 'fr',
        });
    });
});

TypeError: Cannot read properties of undefined (reading 'lang')

I've tried to mock the I18nContext, but with no joy. There must be another way to test this, as it's specifically mentioned in the documentation.


Solution

  • Got the same problem. I don't know if it is the way to mock I18nContext, but I changed the way I pass language in i18nService. I use I18nLang decorator in controller to get language and then pass it in service as paramer.

    Take a look

    Controller:

      @Get(':id')
      async getProductById(
        @Param('id', ParseUUIDPipe) id: string,
        @I18nLang() lang: string,
      ) {
        const product = await this.productsService.getProductById(id, lang);
        return ProductDTO.fromEntity(product);
      }
    

    Service:

    async getProductById(id: string, lang?: string) {
        try {
          const product = await this.productsRepo.findOneOrFail({ id });
          return product;
        } catch (err) {
          throw new BadRequestException(
            this.i18nService.translate(ErrorCodes.NotExists_Product, {
              lang,
            }),
          );
        }
      }
    

    When testing controller it will be easier to pass an argument than somehow mock I18nContext.