React-i18next: Update testing guide

Created on 8 Feb 2019  路  19Comments  路  Source: i18next/react-i18next

After upgrading to react-i18next@10 I tried to update my unit tests to reflect the v10 changes.
Since v10 requires you to wrap your App/Components between a suspense block the docs should/could be changed.

Actually I'm unable to run my tests after refactored them to something like this.

import Enzyme from 'enzyme';
import React, { Suspense } from 'react';
import { Provider } from 'react-redux';

import TimeBar, { Props } from '.';
import store from '../../StateManagement/store';
import i18n from '../../testing/i18.stub';

const setup = () => {
  const props: Props = {
    t: jest.fn(),
    i18n,
    selectedSnapShot: 'all',
    selectedSnapshotChanged: jest.fn()
  };
  const enzymeWrapper = Enzyme.mount(
    <Provider store={store}>
      <Suspense fallback={<div>Loading</div>}>
        <TimeBar {...props} />
      </Suspense>
    </Provider>
  );

  return {
    props,
    enzymeWrapper
  };
};

describe('<TimeBar />', () => {
  it('should render self and subcomponents', () => {
    const { enzymeWrapper } = setup();
    expect(enzymeWrapper.find('TimeBar')).toHaveLength(1);
  });
});

My tests always failing because of an Wrapper suspended while rendering, but no fallback UI was specified. error.
Maybe the docs should be updated how to test i18next correctly with Suspense.

Most helpful comment

https://github.com/i18next/react-i18next/tree/master/example/test-jest --> see the __mocks__ folder

All 19 comments

Will do things one by one - beside i18next i got a family and run a business. So things will come when they come - feel free to contribute to the docs.

I guess testing should be no issue at all just mocking i18n away -> if there is no need to load translations there won't be a not ready state triggering a suspense -> if you're loading translations during unit tests i would say somethings wrong with testing

https://github.com/i18next/react-i18next/tree/master/example/test-jest --> see the __mocks__ folder

Sure, it is more like a reminder :)

I don't load translations during tests as described in your (now updated) guid: https://github.com/i18next/react-i18next/blob/master/example/test-jest/src/i18n.js

But the issue with the Wrapper suspended while rendering, but no fallback UI was specified error is still there. I tried to wrap the component inside a <I18nextProvider> tag but I'm facing the same error. I tried FCMs and normal Components.

馃し鈥嶁檪锔弉ot running into this as in the sample the useTranslation gets mocked so never throws a Promise triggering a suspense

Hello, I'm also trying to upgrade to version 10 without mocking react-i18next, but just using the sample i18n configuration found in the doc and passing it down using an I18nextProvider component.

I wrote a simple custom test render function extending react-testing-library's render.

This is a simplified version:

// test/render.js
import React, { Suspense } from 'react';
import i18n from 'i18next';
import { I18nextProvider } from 'react-i18next';
import { render as rtlRender } from 'react-testing-library';

i18n.init({
  fallbackLng: 'cimode',
  debug: false,
  saveMissing: false,

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

  // react i18next special options (optional)
  react: {
    wait: true,
    nsMode: 'fallback', // set it to fallback to let passed namespaces to translated hoc act as fallbacks
  },
});

export default (ui, options) => ({
   ...rtlRender(
     <I18nextProvider i18n={i18n}>
       <Suspense fallback={<div>Loading translations...</div>}>
         {ui}
       </Suspense>
    </I18nextProvider>,
    options
  )
})

Unfortunately this also seems to always render the fallback element <div>Loading translations...</div> and I can't get a meaningful snapshot.

Can you correct my approach?

Thanks a lot in advance, and thanks for your great work.

Can you please retry with [email protected]

Hi @jamuhl, thanks for your help, unfortunately I'm still experiencing the same result even on 10.0.2: <div>Loading translations...</div> is rendered.

set lng: 'cimode' and fallbackLng: false

but actually should work...did you check really v10.0.2 was installed? deleted node_modules, package-lock.json

I was facing the same problem as I'm also using a custom react-test-library render function with the provider wrapping the ui. By looking at the source of the lib it looks like the suspense was triggered because isInitialized was not true (you are forcing it by creating a i18n instance manually but I didn't want to do that).

So after some fiddling with the options during the i18n instance creation, I found that the only was to have isInitialized was to add a resource _at the creation time_. Before I was using addResourceBundle after the initialization.

So instead of doing this.

const i18ninstance = i18n
    .use(initReactI18next)
    .init({
      fallbackLng: false,
      lng: 'cimode',
      backend: undefined,
      interpolation: {
        escapeValue: false,
      },
      react: {
        wait: false,
      },
    });
  i18nInstance.addResourceBundle('en', 'core', { test: 'test' });

I had to do this

const i18ninstance = i18n
    .use(initReactI18next)
    .init({
      // [...]
      resources: {
        en: {
          core: {
            test: 'test',
          },
        },
      },
    });

I don't know if it is a bug or not since it looks to me like the result should be equivalent but I could avoid the Suspense triggering only with the resources property in the object passed to the init function

Depending on const i18ninstance = i18n i18next version (> v14) i18ninstance will now be a Promise

adding resources on init will avoid loading attempt -> which is async. So it's expected behaviour. Not really sure why behaviour is different from v9 to v10 -> seems kind of you had luck in v9 with timings...

Hello, adding the resources key at i18n creation time got it working as expected (it is not substituting the translations, but just outputting the translation keys) and I was able to fix the rest of the Jest test suite. Unfortunately, now I am getting weird behaviour (the page takes much longer to load and therefore times out) from cypress. I will keep working on this branch as time allows!

Just in case anyone else trips up on this, the /__mocks__/react-i18next.js should stay as a JS file. When I adapted it to TypeScript, the mocks wouldn't work for me (it would break on the useTranslation hook).

Closing this for now...as not an issue i guess...

Still accepting PR to the documentation, eg. https://react.i18next.com/misc/testing

@lvl99 How did you mock the translations? In my case, all the translations are coming from a backend

Hi everyone,
I added something like this to mock the translations i read from backend.

const onboarding = JSON.stringify({
  disclosure: {
    intro: {
      title: 'My title',
      description: 'My descritpion',
      estimate: 'Done in {{minutes}} minutes',
    },
  },
});
jest.mock('react-i18next', () => ({
  withTranslation: () => (Component: any) => {
    Component.defaultProps = {...Component.defaultProps, t: (onboarding: string) => onboarding};
    return Component;
  },
}));

Related to component, it looks like this, in its beginning and export section:

const MyComponent: React.FC<WithTranslation & MyProps> = ({
  t,
}) => {
...
...
}
export default withRouteGuard({
  guard: () => !isGuestUser(),
  fallbackPath: {pathname: '/'},
})(withTranslation(['onboarding'])(MyComponent));

In snapshot i get no translated text, instead i get disclosure.intro.description, disclosure.intro.estimate, disclosure.intro.description.

Is that expected? I am curious: is there a way to get translations?

Was this page helpful?
0 / 5 - 0 ratings