React测试套件报translateAttribute不是函数的原因与解决
问题背景
我用Create-React-App创建React应用,采用testing-library/react搭建测试套件。编写的登录组件测试用例运行时抛出错误:TypeError: (0 , _Locale.translateAttribute) is not a function。注释调用translateAttribute的代码后测试通过,但实际应用功能失效;将attribute改为() => translateAttribute(type)后测试通过,但翻译文本无法正常显示。需要解决两个问题:
- 为何该错误仅在测试时出现?
- 如何无需注释代码即可让测试通过?
相关代码
测试代码
import React from 'react'; import {render, screen} from '@testing-library/react'; import userEvent from '@testing-library/user-event' import Login from '../../scenes/Account/Login'; import { BrowserRouter as Router } from 'react-router-dom' import { ThemeProvider } from 'styled-components'; import { theme, store } from '../../config' import '@testing-library/jest-dom'; import { Provider } from 'react-redux' test('the connect button is disabled until a username and password is entered', async () => { render(<Router><ThemeProvider theme={theme}><Provider store={store}><Login /></Provider></ThemeProvider></Router>); const usernameInput = screen.getByRole('textbox'); const passwordInput = screen.getByTestId('password') const connectButton = screen.getByRole('button', { name: /connexion/i}) expect(connectButton).toBeDisabled(); await userEvent.type(usernameInput, 'test'); expect(connectButton).toBeDisabled(); await userEvent.type(passwordInput, 'test'); expect(connectButton).toBeEnabled(); })
调用代码
import t, { translateAttribute } from '../services/Locale' export const attribute = translateAttribute(type) // 注释该行测试通过
translateAttribute定义
export const translateAttribute = resource => (attribute, values = {}) => translate(`entities.${resource}.attributes.${snakeCase(attribute)}`, values)
问题原因
- 测试环境初始化差异:生产环境中翻译服务(
translate函数)会在模块加载前完成初始化,但测试环境下模块加载顺序不同,translateAttribute依赖的translate函数在执行translateAttribute(type)时还未就绪,导致柯里化过程出错,抛出“不是函数”的错误。 - 柯里化函数执行时机问题:
translateAttribute是柯里化函数,你在模块级别直接执行translateAttribute(type),相当于在模块加载时就触发了内部translate的调用,而测试环境中此时翻译服务还未完成配置。
解决方案
方案1:模拟Locale模块(推荐)
在测试文件顶部用Jest模拟Locale模块的translateAttribute,避免依赖未初始化的翻译服务:
// 放在测试文件的import语句之后 jest.mock('../../services/Locale', () => { const originalModule = jest.requireActual('../../services/Locale'); return { ...originalModule, // 模拟translateAttribute,返回一个简单的翻译函数(比如直接返回属性名) translateAttribute: () => (attr) => attr }; });
这种方式既保留了原模块的其他功能,又替换了测试环境下的translateAttribute实现,不会影响生产环境的代码逻辑。
方案2:延迟柯里化函数的执行
修改调用代码,将attribute改为函数形式,延迟到组件使用时再执行翻译逻辑:
import t, { translateAttribute } from '../services/Locale' // 改为函数,在组件中使用时再调用 export const attribute = (attr, values = {}) => translateAttribute(type)(attr, values)
同时需要同步修改组件中使用attribute的地方,原本的attribute('username')调用方式可以保持不变(因为现在attribute本身就是函数)。这种方式避免了模块加载时的提前执行,适配测试环境的初始化顺序。
方案3:确保测试环境中翻译服务就绪
检查你的Locale模块是否依赖Redux store或全局配置,在测试渲染前先初始化相关状态:
比如在测试中提前dispatch设置语言的action,或者在Locale模块中添加测试环境的兼容逻辑,确保translate函数在测试时不会抛出错误。
验证
修改后重新运行测试,确认报错消失;同时启动开发环境,检查登录组件的翻译文本是否正常显示。
内容的提问来源于stack exchange,提问作者adarm9

