十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

react-i18next 测试实战指南:基于 Jest 与 Testing Library 的 example/test-jest 全解

react-i18next 测试实战指南:基于 Jest 与 Testing Library 的 example/test-jest 全解 react-i18next 测试实战指南基于 Jest 与 Testing Library 的 example/test-jest 全解【免费下载链接】react-i18nextInternationalization for react done right. Using the i18next i18n ecosystem.项目地址: https://gitcode.com/gh_mirrors/re/react-i18next导读本文以 example/test-jest 示例项目为主体系统讲解在 React i18next 生态中如何用 Jest 与testing-library/react编写可靠的国际化i18n测试包括如何通过src/__mocks__/react-i18next.js自动 mockt、Trans等翻译 API如何在setupTests.js中注册 jest-dom 匹配器或选择真初始化而非打桩以及jest.config.js/babel.config.js的完整配置。读完本文你将掌握一套可直接复制的 react-i18next 组件测试方案并理解每一条 mock 分支背后的源码级动机。示例项目定位与整体结构example/test-jest是 react-i18next 官方仓库中的独立可运行示例其定位非常明确展示在不引入真实 i18next 实例的情况下如何用最轻量的方式测试使用 i18n 的组件。项目同时以 Vite 作为开发/构建工具以 Jest 作为测试运行器兼顾了能跑起来与能测起来两个场景。example/test-jest/ ├── babel.config.js # Jest 使用的 Babel 配置 ├── jest.config.js # Jest 配置roots、setup、moduleNameMapper、transform ├── src/ │ ├── __mocks__/react-i18next.js # 核心react-i18next 的自动 mock │ ├── setupTests.js # jest-dom 注册 真初始化替代方案示例 │ ├── test-utils/fileMock.js # css/svg 静态资源 stub │ ├── i18n.js # 真实应用入口中的 i18next 初始化仅作对照 │ ├── App.jsx / App.test.jsx # 基础渲染测试 │ ├── UseTranslation.jsx/.test.jsx # useTranslation Hook 测试 │ ├── Trans.jsx/.test.jsx # Trans 组件测试 │ ├── Translation.jsx/.test.jsx # Translation 渲染 prop 测试 │ ├── WithTranslation.jsx/.test.jsx# withTranslation HOC 测试 │ └── UseTranslationWithInterpolation.jsx/.test.jsx其中package.json的依赖组合为reactreact-i18nexti18next运行时jest、jest-environment-jsdom、babel-jest、testing-library/react、testing-library/jest-dom测试时见 example/test-jest/package.json。核心思路用自动 Mock 隔离翻译层README 的第一条核心指引是./src/__mocks__contains the react-i18next mock (mocks translate and Trans; mock other components as needed!!!)这正是本示例的灵魂将react-i18next包整体替换为一个返回 key 本身的桩实现。这样测试既不依赖真实语言资源文件也不依赖 i18next 初始化时机组件只要收到t函数即可完成渲染断言。Jest 自动 mock 的触发机制Jest 规定只要在__mocks__目录下存在与包名同名的文件src/mocks/react-i18next.js那么当测试代码import { useTranslation } from react-i18next时Jest 会自动优先使用该 mock无需显式jest.mock()调用。关键前提在于 jest.config.js 中的一行export default { testEnvironment: jsdom, // roots must include src so src/__mocks__/react-i18next.js auto-mocks the package roots: [rootDir/src], setupFilesAfterEnv: [rootDir/src/setupTests.js], moduleNameMapper: { \\.(css|svg)$: rootDir/src/test-utils/fileMock.js, }, transform: { ^.\\.[jt]sx?$: babel-jest, }, };roots必须包含src否则src/__mocks__不会被 Jest 识别为自动 mock 目录——注释里特别强调了这一点。同时testEnvironment: jsdom提供 DOM 环境配合 Testing Library 的render。setupFilesAfterEnv在测试文件执行前加载setupTests.js。moduleNameMapper把.css/.svg导入映射到 src/test-utils/fileMock.js内容为module.exports test-file-stub避免 Jest 解析不了样式与图片资源。transform所有[jt]sx?文件交给babel-jest其 Babel 预设见 babel.config.jsbabel/preset-envtargets: { node: current }与babel/preset-reactruntime: automatic即自动 JSX runtime无需显式import React也能用 JSX不过示例代码仍保留了import React。mock 逐项拆解src/mocks/react-i18next.js 的完整内容值得逐行研读const React require(react); const reactI18next require(react-i18next); const hasChildren node node (node.children || (node.props node.props.children)); const getChildren node node node.children ? node.children : node.props node.props.children; const renderNodes reactNodes { if (typeof reactNodes string) { return reactNodes; } return Object.keys(reactNodes).map((key, i) { const child reactNodes[key]; const isElement React.isValidElement(child); if (typeof child string) { return child; } if (hasChildren(child)) { const inner renderNodes(getChildren(child)); return React.cloneElement(child, { ...child.props, key: i }, inner); } if (typeof child object !isElement) { return Object.keys(child).reduce((str, childKey) ${str}${child[childKey]}, ); } return child; }); }; const useMock [k k, { changeLanguage: () new Promise(() {}) }]; useMock.t k k; useMock.i18n { changeLanguage: () new Promise(() {}) }; module.exports { // this mock makes sure any components using the translate HoC receive the t function as a prop withTranslation: () Component props Component t{k k} {...props} /, Trans: ({ children, i18nKey }) !children ? i18nKey : Array.isArray(children) ? renderNodes(children) : renderNodes([children]), Translation: ({ children }) children(k k, { i18n: {} }), useTranslation: () useMock, // mock if needed I18nextProvider: reactI18next.I18nextProvider, initReactI18next: reactI18next.initReactI18next, setDefaults: reactI18next.setDefaults, getDefaults: reactI18next.getDefaults, setI18n: reactI18next.setI18n, getI18n: reactI18next.getI18n, };各 API 的 mock 策略如下react-i18next APImock 行为设计意图useTranslation()返回[k k, { changeLanguage }]并附加t k k、i18n.changeLanguageHook 返回的t原样返回 key便于断言渲染了哪个 keywithTranslation()返回高阶组件向被包裹组件注入t{k k}保证 HOC 用法下组件能收到tpropTrans无children时渲染i18nKey有children时递归渲染子节点兼容Trans i18nKey...default text/Trans两种形态Translation调用children(k k, { i18n: {} })渲染 prop 模式直接拿到tI18nextProvider/initReactI18next等直接透传真实实现需要时再替换其余保持真实导出注意useMock的实现技巧把t和i18n直接挂到数组对象上这样无论使用者按数组解构const [t] useTranslation()还是按const { t, i18n } useTranslation()都能取到值。Trans 的 children 递归渲染原理renderNodes是本 mock 中最重的逻辑它的作用是在没有真实 i18next 解析器的情况下把Trans的 JSX children 摊平成可渲染的 React 节点。它处理了四类情况字符串节点直接返回字符串子元素直接返回带 children 的元素递归摊平后通过React.cloneElement重挂key普通对象如翻译资源中的嵌套对象把对象值拼接为字符串。对应到真实实现Trans的 children 摊平逻辑在源码 src/Trans.js 与 src/TransWithoutContext.js 中由nodesToString/renderNodes完成见 test/trans.render.spec.jsx 对真实渲染行为的覆盖mock 版刻意保持只保留结构、不解析插值的最小行为让测试聚焦在 DOM 结构而非翻译结果上。setupTests.jsjest-dom 与真初始化两条路线README 的第二条指引./src/setupTests.jsregisters thetesting-library/jest-dommatchers (and shows how to init i18next instead of stubbing)src/setupTests.js 做了两件事import testing-library/jest-dom; // without stubbing do this: // import i18n from i18next; // import { initReactI18next } from react-i18next; // i18n.use(initReactI18next).init({ // lng: en, // fallbackLng: en, // ns: [translations], // defaultNS: translations, // interpolation: { // escapeValue: false, // not needed for react!! // }, // resources: { en: { translations: {} } }, // });第一行注册testing-library/jest-dom的增强匹配器例如测试中使用的toHaveTextContentexpect(container.querySelector(div)).toHaveTextContent(description.part2);见 src/UseTranslation.test.jsx 与 src/Trans.test.jsx。注释掉的段落则展示了第二条路线不 mock直接真初始化 i18next。这在需要验证插值、复数、命名空间等真实翻译行为时很有用。它的初始化参数与真实应用入口 src/i18n.js 几乎一致i18n .use(Backend) // i18next-http-backend .use(LanguageDetector) // i18next-browser-languagedetector .use(initReactI18next) .init({ fallbackLng: en, ns: [translations], defaultNS: translations, debug: true, interpolation: { escapeValue: false, // not needed for react!! }, });两处都使用了interpolation.escapeValue: false——因为 React 本身会对 JSX 文本做转义i18next 无需再转义一次这是 react-i18next 初始化配置的惯例。区别仅在于测试版用resources内联翻译资源应用版用 HTTP Backend 语言探测器从public/locales异步加载。测试文件体系覆盖 react-i18next 的全部用法README 提到./src/App.test.jsxbasic render test./src/*.test.jsxtests usingtesting-library/react示例中的每个测试文件都对应 react-i18next 的一种典型用法形成一个完整的覆盖矩阵1. 基础渲染冒烟测试App.test.jsxsrc/App.test.jsx 使用react-dom/client的createRoot直接在测试中挂载/卸载整个App /验证不崩溃这一最低门槛。真实 src/App.jsx 中同时用到了withTranslationHOC、Trans、useTranslation以及Suspensefallback对应 i18next 异步加载时的挂起态一个测试即可覆盖多条代码路径。2. useTranslation HookUseTranslation.test.jsx被测组件 src/UseTranslation.jsxconst { t } useTranslation(); return div{t(description.part2)}/div;由于 mock 中useTranslation返回的t是k k因此测试断言渲染文本等于 key 本身expect(container.querySelector(div)).toHaveTextContent(description.part2);这种断言 key的写法使得翻译文案修改如把description.part2改为别的 key会直接导致测试失败从而把 key 名当作稳定的契约来守护。3. Trans 组件Trans.test.jsx示例中还提供了带内联元素的Trans用例strong包裹的description.bold测试用toHaveTextContent在strong元素上断言验证 mock 版Trans的renderNodes能正确保留嵌套元素结构。4. 其余覆盖仓库中还有Translation渲染 prop、WithTranslationHOC、UseTranslationWithInterpolation插值场景三组组件与测试共同保证 react-i18next 的每种 API 形态在测试环境下都有对应验证路径见 example/test-jest/src 目录下的Translation.jsx、WithTranslation.jsx、UseTranslationWithInterpolation.jsx及其同名.test.jsx文件。运行方式测试与开发/构建命令README 给出了完整命令对应 example/test-jest/package.json 的 scripts命令作用npm test运行 Jest配置来自jest.config.js/babel.config.js执行全部*.test.jsxnpm start或npm run dev启动 Vite 开发服务器默认地址http://localhost:5173npm run buildVite 生产构建产物输出到dist/npm run preview本地预览构建产物整个测试链路无需启动任何后端jsdom 环境 自动 mock 使测试在毫秒级内完成与 Vite 的开发/构建链路完全解耦。小结何时用 Mock何时真初始化从本示例可以提炼出两条可落地的测试策略默认策略——自动 mock只要关心组件是否正确渲染、是否正确使用翻译 key就用src/__mocks__/react-i18next.js这套桩实现。它速度快、不依赖资源文件且能通过断言 key 文本把翻译 key 变成可回归的契约。进阶策略——真初始化当需要验证插值格式、复数规则、命名空间回退等真实翻译行为时参考 src/setupTests.js 中注释的i18n.init方案用resources内联小规模翻译资源即可无需引入网络后端。两种策略共享同一套 Jest 基建roots指向src以启用自动 mock、setupFilesAfterEnv加载 jest-dom、moduleNameMapper处理静态资源、babel-jest负责 JSX 编译。这套配置可以直接迁移到任何 Vite React react-i18next 项目中作为团队国际化测试的起步模板。【免费下载链接】react-i18nextInternationalization for react done right. Using the i18next i18n ecosystem.项目地址: https://gitcode.com/gh_mirrors/re/react-i18next创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表