【问题标题】:How do I mock react-i18next and i18n.js in jest?我如何开玩笑地模拟 react-i18next 和 i18n.js?
【发布时间】:2017-12-14 16:58:22
【问题描述】:

package.json

"moduleNameMapper": {
  "i18next": "<rootDir>/__mocks__/i18nextMock.js"
}

i18n.js

import i18n from 'i18next'
import XHR from 'i18next-xhr-backend'
// import Cache from 'i18next-localstorage-cache'
import LanguageDetector from 'i18next-browser-languagedetector'

i18n
  .use(XHR)
  // .use(Cache)
  .use(LanguageDetector)
  .init({
    fallbackLng: 'en',
    // wait: true, // globally set to wait for loaded translations in translate hoc
    lowerCaseLng: true,
    load: 'languageOnly',
    // have a common namespace used around the full app
    ns: ['common'],
    defaultNS: 'common',
    debug: true,

    // cache: {
    //   enabled: true
    // },

    interpolation: {
      escapeValue: false, // not needed for react!!
      formatSeparator: ',',
      format: function (value, format, lng) {
        if (format === 'uppercase') return value.toUpperCase()
        return value
      }
    }
  })

export default i18n

i18nextMock.js

/* global jest */
const i18next = jest.genMockFromModule('react-i18next')
i18next.t = (i) => i
i18next.translate = (c) => (k) => k

module.exports = i18next

出于某种原因,开玩笑的单元测试没有得到组件。

这是一个单元测试:

import React from 'react'
import { Provider } from 'react-redux'
import { MemoryRouter } from 'react-router-dom'
import { mount } from 'enzyme'

import { storeFake } from 'Base/core/storeFake'
import Container from '../container'

describe('MyContainer (Container) ', () => {
  let Component;

  beforeEach(() => {
    const store = storeFake({})

    const wrapper = mount(
      <MemoryRouter>
        <Provider store={store}>
          <Container />
        </Provider>
      </MemoryRouter>
    )

    Component = wrapper.find(Container)
  });

  it('should render', () => {
    // Component is undefined here
    expect(Component.length).toBeTruthy()
  })
})

【问题讨论】:

    标签: reactjs jestjs i18next react-i18next


    【解决方案1】:

    您不需要模拟 t 函数,只需要 translate 一个。对于第二个,参数的用法很混乱,而且你需要返回一个组件。

    我能够让它在我的项目中运行。这是我的模拟文件和我的 Jest 配置

    Jest 配置

    "moduleNameMapper": {
        "react-i18next": "<rootDir>/__mocks__/reacti18nextMock.js"
    }
    

    模拟react-i18next的源代码

    /* global jest */
    import React from 'react'
    
    const react_i18next = jest.genMockFromModule('react-i18next')
    
    const translate = () => Component => props => <Component t={() => ''} {...props} />
    
    react_i18next.translate = translate
    
    module.exports = react_i18next
    

    【讨论】:

    • 如何在测试文件中使用??
    【解决方案2】:

    我在玩笑测试中使用了 Atemu 的 anwser,但最终在模拟中得到以下一行:

    module.exports = {t: key => key};
    

    还修改了 jest 配置,因为我从 'i18next' 导入了 't':

    "moduleNameMapper": {
        "i18next": "<rootDir>/__mocks__/reacti18nextMock.js"
    }
    

    【讨论】:

      【解决方案3】:

      在我的例子中,使用带有 TypeScript 的 useTranslation 钩子,如下所示:

      const reactI18Next: any = jest.createMockFromModule('react-i18next');
      
      reactI18Next.useTranslation = () => {
        return {
          t: (str: string) => str,
          i18n: {
            changeLanguage: () => new Promise(() => {}),
          },
        };
      };
      
      module.exports = reactI18Next;
      
      export default {};
      

      jest.config.ts:

      const config: Config.InitialOptions = {
        verbose: true,
        moduleNameMapper: {
          'react-i18next': '<rootDir>/__mocks__/react-i18next.ts',
        },
      };
      

      【讨论】:

      • 我可以按照这个答案配置模拟文件。
      • 如何在测试文件中使用??.
      • 它会自动与此配置一起使用 @Nik 。您只需开始测试,除非您手动取消模拟,否则 ti18n 函数将被模拟
      【解决方案4】:

      “按原样”返回密钥并不是最好的。我们使用英文文本作为键,最好“评估”我们传入的值(即 t('{{timePeriod}} left') 评估为:'5剩余天数')。在这种情况下,我创建了一个辅助函数来执行此操作。以下是所需的笑话和额外文件:

      Jest 配置(即 jest.config.js):

        moduleNameMapper: {
          'react-i18next': '<rootDir>/src/tests/i18nextReactMocks.tsx',
          'i18next': '<rootDir>/src/tests/i18nextMocks.ts',
          // ...
        },
      

      i18nextMocks.ts:

      function replaceBetween(startIndex: number, endIndex: number, original: string, insertion: string) {
        const result = original.substring(0, startIndex) + insertion + original.substring(endIndex);
        return result;
      }
      
      export function mockT(i18nKey: string, args?: any) {
        let key = i18nKey;
      
        while (key.includes('{{')) {
          const startIndex = key.indexOf('{{');
          const endIndex = key.indexOf('}}');
      
          const currentArg = key.substring(startIndex + 2, endIndex);
          const value = args[currentArg];
      
          key = replaceBetween(startIndex, endIndex + 2, key, value);
        }
      
        return key;
      }
      
      const i18next: any = jest.createMockFromModule('i18next');
      i18next.t = mockT;
      i18next.language = 'en';
      i18next.changeLanguage = (locale: string) => new Promise(() => {});
      
      export default i18next;
      

      i18nextReactMocks.tsx:

      import React from 'react';
      import * as i18nextMocks from './i18nextMocks';
      
      export const useTranslation = () => {
        return {
          t: i18nextMocks.mockT,
          i18n: {
            changeLanguage: () => new Promise(() => {}),
          },
        };
      };
      
      export const Trans = ({ children }) => <React.Fragment>{children}</React.Fragment>;
      

      我会免费提供模拟单元测试:)

      import * as i18nextMocks from './i18nextMocks';
      
      describe('i18nextMocks', () => {
        describe('mockT', () => {
          it('should return correctly with no arguments', async () => {
            const testText = `The company's new IT initiative, code named Phoenix Project, is critical to the
              future of Parts Unlimited, but the project is massively over budget and very late. The CEO wants
              Bill to report directly to him and fix the mess in ninety days or else Bill's entire department
              will be outsourced.`;
      
            const translatedText = i18nextMocks.mockT(testText);
      
            expect(translatedText).toBe(testText);
          });
      
          test.each`
            testText                            | args                                          | expectedText
            ${'{{fileName}} is invalid.'}       | ${{ fileName: 'example_5.csv' }}              | ${'example_5.csv is invalid.'}
            ${'{{fileName}} {is}.'}             | ${{ fileName: '   ' }}                        | ${'    {is}.'}
            ${'{{number}} of {{total}}'}        | ${{ number: 0, total: 999 }}                  | ${'0 of 999'}
            ${'There was an error:\n{{error}}'} | ${{ error: 'Failed' }}                        | ${'There was an error:\nFailed'}
            ${'Click:{{li}}{{li2}}{{li_3}}'}    | ${{ li: '', li2: 'https://', li_3: '!@#$%' }} | ${'Click:https://!@#$%'}
            ${'{{happy}}?y✔{{sad}}{{laugh}}'}  | ${{ happy: '?', sad: '?', laugh: '?' }}    | ${'??y✔??'}
          `('should return correctly while handling arguments in different scenarios', ({ testText, args, expectedText }) => {
            const translatedText = i18nextMocks.mockT(testText, args);
      
            expect(translatedText).toBe(expectedText);
          });
        });
      
        describe('language', () => {
          it('should return language', async () => {
            const language = i18nextMocks.default.language;
      
            expect(language).toBe('en');
          });
        });
      });
      

      【讨论】:

        猜你喜欢
        • 2018-07-25
        • 1970-01-01
        • 2022-09-27
        • 2021-05-27
        • 1970-01-01
        • 2023-03-27
        • 2020-11-18
        • 1970-01-01
        • 2021-07-02
        相关资源
        最近更新 更多