'react-i18next:: You will need to pass in an i18next instance by using initReactI18next warning thrown in Jest unit test

I am using react-i18next in my app to great effect but when I run the unit tests against my component:

const OptionList = ({
  definition,
  name,
  status = EMutationStatus.IDLE,
  onChange = () => null,
  value = [],
}: IOptionListProps): React.ReactElement => {
  const { t } = useTranslation();
  const { options } = definition;
  return (
    <Select
      name={name}
      data-testid="optionList"
      id={name}
      placeholder={t('COMMON.PLEASE_SELECT')}
      onChange={e => onChange(e.currentTarget.value)}
      defaultValue={value[0]}
      disabled={status === EMutationStatus.LOADING}
    >
      {options.map((option: string): React.ReactElement => {
        return (
          <option key={option} value={option}>
            {option}
          </option>
        );
      })}
    </Select>
  );
};

It throws the following warning when running the unit test suite:

react-i18next:: You will need to pass in an i18next instance by using initReactI18next

I am a bit lost as I have set up react-i18next for unit testing in a custom render function for react-testing-library.

My i18n instance for tests:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

i18n.use(initReactI18next).init({
  lng: 'cimode', // setting lng to 'cimode' will cause the t function to always return the key.
  // ----- ^
  // have a common namespace used around the full app
  ns: ['translations'],
  defaultNS: 'translations',

  interpolation: {
    escapeValue: false, // not needed for react!!
  },

  resources: { en: { translations: {} }, zh: { translations: {} }, th: { translations: {} } },
});

export default i18n;

And my custom render function is as follows:

export function render(
  ui: RenderUI,
  { wrapper, ...options }: RenderOptions = {}
): RenderResult {
  if (!wrapper) {
    // eslint-disable-next-line no-param-reassign
    wrapper = ({ children }) => (
      <BrowserRouter>
        <ChakraProvider resetCSS theme={theme}>
          <QueryClientProvider client={testQueryClient}>
            <I18nextProvider i18n={i18n}>{children}</I18nextProvider>
          </QueryClientProvider>
        </ChakraProvider>
      </BrowserRouter>
    );
  }

  return defaultRender(ui, { wrapper, ...options });
}

It is only a warning so not a big deal but am confused as to why this is throwing here as I believe I have set up the Provider as outlined in the docs here:

https://react.i18next.com/misc/testing

An anyone help me get rid of this warning?

UPDATE

I tried adding the suggested jest mock of the useTranslation hook from the same testing link:

jest.mock('react-i18next', () => ({
  // this mock makes sure any components using the translate hook can use it without a warning being shown
  useTranslation: () => {
    return {
      t: (str: string): string => str,
    };
  },
}));

But still I get the same warning.



Solution 1:[1]

Did you try to call i18n.init() inside beforeEach hook? And i18n should be the instance of your instantiated i18n instance.

The only difference that in my case I have named export of the i18n instance.

import { i18n } from "../../services/Internationalization";

describe('testSuite', () => {
   beforeEach(() => {
    i18n.init();
  });
});

And in i18n.ts I have to do:

i18n.use(initReactI18Next).init(...some configs);
export { i18n };

Solution 2:[2]

One modification for RyanP13s proposal, I return an array as expected return value from useTranslation(), works now fine for me:

jest.mock('react-i18next', () => ({
  useTranslation: () => {
    return [/* t */ (str: string): string => str, /* i18n */ { language: 'cimode' }]
  },
}))

Solution 3:[3]

This worked for me. You can find more info in the official documentation.

jest.mock('react-i18next', () => ({
  // this mock makes sure any components using the translate hook can use it without a warning being shown
  useTranslation: () => {
    return {
      t: (str) => str,
      i18n: {
        changeLanguage: () => new Promise(() => {}),
      },
    };
  },
}));

Solution 4:[4]

While it's not the official solution, I found that creating an instance of i18n specifically for testing worked nicely. It renders the actual strings and you don't have to mock useTranslation, which is the usual recommendation.

Create a file called i18nForTests.

// i18nForTests.js

import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import { fooTranslation } from '/translations/foo'
import { barTranslation } from '/translations/bar'

i18n
  .use(initReactI18next)
  .init({
    lng: 'foo',
    fallbackLng: 'foo',
    ns: ['foobar'],
    defaultNS: 'foobar',
    debug: true,
    resources: {
      'foo': fooTranslation,
      'bar': barTranslation
    }
  })
  
 export default i18n

Then import and initialize this heinous hackery in jest.setup.js.

Note: if you don't have jest.setup.js, use this in whichever setup file you do have (see jest.config.js > setupFiles). If you don't have any setup files, create one.

// jest.setup.js

import i18n from 'i18nForTests'

i18n.init()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Andrew Trends
Solution 2 matthias
Solution 3 MWO
Solution 4 John