16
votes

I am making unit tests with jest and react-testing-library for my frontend application which is done with React. My unit tests worked nicely before I added the internationalization with react-i18next -library. Now when I run the tests, it seems that it doesn't find/use the translation files and all places where there should read something, are left empty. I'm using the newest react version with hooks and instead of React.Component I am using this kind of "const-components":

    const ComponentName = ({t}) => {
        return(
          <p>{t('example')}</p>
        )}
      export default ComponentName;

The internationalization works perfectly in the actual page but just that the unit tests fail due to not using the translation-file so I think the problem is with correctly mocking the translation files. I am only finding some suggestion solutions for the older react using this.variableName -type of solutions, which however doesn't help me much.

I have tried to mock it with jest.fn(), but I am not sure which function is the one, which I should mock and how to utilize the useTranslation() -function correctly from the tests.

    import React from 'react';
    import { useTranslation, Trans } from 'react-i18next';
    import { render } from '@testing-library/react';
    import ComponentName from './ComponentName';

    import '../locales/i18n';

    test('renders all documents in the list', () => {
      const mockUseTranslation = jest.fn();

      const { t, i18n } = mockUseTranslation();

      // const t = jest.fn();
      const c = render(<ComponentName t={t} />);
      expect(c.getByText('Translation File Title')).toBeDefined();
      expect(
        c.getAllByText(
          'Lorem ipsum'
        ).length
      ).toBe(3);
    });

Error message: Unable to find an element with the text: Translation File Title. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

So in short: the place, which should contain certain text is now totally empty.

2
I don't think you should mock the i18n library. If you use debug what do you see in the page? Is the text empty? Does the i18n library use a provider to work?Giorgio Polvara - Gpx
I'm not using redux, provider or store in my app so this doesnt really help me. Also those tests are with enzyme and I'm using react-testing-library. If I use the debug() tool it looks empty (in the place, which there should be translated text) as I mentioned before :/ So somehow the translation should be mocked or brought into the test.Sonja Laurila

2 Answers

10
votes

You should not mock the translation, instead render the component with translation library as Higher Order Component, for example;

import React from 'react';
import i18n from '../../../i18n' // your i18n config file
import { render } from '@testing-library/react';
import ComponentName from './ComponentName';
import { I18nextProvider } from 'react-i18next'

test('renders all documents in the list', () => {
    const c = render(
      <I18nextProvider i18n={i18n}> // actually give translation to your component
         <ComponentName />
      </I18nextProvider>
    );
    // example if you have a key called example
    expect(c.getByText(i18n.getDataByLanguage('en').translation.example)).toBeDefined(); 
});

Instead of calling your translation texts with i18n.getDataByLanguage('en') , you can give the default translation of your project, if it is French call it by i18n.getDataByLanguage('fr').

Also change your component like this, instead of taking useTranslation hook from props, take it inside the component with hooks

ComponentName.jsx

import { useTranslation } from 'react-i18next'

const ComponentName = () => {
  const { t } = useTranslation()

  return(
    <p>{t('example')}</p>
  )}

export default ComponentName;
7
votes

Eventually I got the mock working like this (in App.js):

jest.mock('react-i18next', () => ({
  useTranslation: () => ({
    t: key => key,
    i18n: { changeLanguage: jest.fn() }
  })
}));

In case somebody needs this.

Additionally inside components I was just using t={key=>key}, which enabled queries like this: expect(c.getByText('json.field.in.translation')).toBeDefined();