React按钮点击调用Mock函数测试失败问题求助
问题:Mock函数testClick未被调用的原因及解决办法
我正在自学用react-testing library和Jest做React测试,始终搞不懂为什么Mock函数testClick没被调用。查了不少文章和视频但大多过时了,附上相关代码和测试失败结果,求指点。
Products.js组件代码
import React, { useState } from 'react'; import { fetchProducts } from '../service'; const Products = () => { const [product, setProduct] = useState(''); const handleSubmit = (event) => { event.preventDefault(); fetchProducts(); }; const hasBeenClicked = () => { console.log('clicked!'); }; return ( <form onSubmit={handleSubmit}> <label> <input type="text" value={product} onChange={(e) => setProduct(e.target.value)} /> </label> <input type="submit" onClick={hasBeenClicked} data-testid="button" label="Search" /> </form> ); }; export default Products;
Products.test.js测试代码
import React from 'react'; import { render, screen, cleanup, fireEvent } from '@testing-library/react'; import Products from './Products'; test('Button click triggers click event', () => { const testClick = jest.fn(); const { getByTestId } = render(<Products click={testClick} />); const button = getByTestId('button'); fireEvent.click(button); expect(testClick).toBeCalled(); });
测试失败结果
PASS src/App.test.js FAIL src/components/Products.test.js ● Console console.log clicked! at apply (src/components/Products.js:13:13) ● Button clicks triggers click event expect(jest.fn()).toBeCalled() Expected number of calls: >= 1 Received number of calls: 0 11 | fireEvent.click(button); 12 | > 13 | expect(testClick).toBeCalled(); | ^ 14 | }); 15 | at Object.<anonymous> (src/components/Products.test.js:13:21) Test Suites: 1 failed, 1 passed, 2 total Tests: 1 failed, 1 passed, 2 total Snapshots: 0 total Time: 3.18 s, estimated 4 s
问题原因与解决办法
核心原因
你的Products组件根本没有接收click这个props,按钮的onClick绑定的是组件内部定义的hasBeenClicked函数,和你在测试中传入的testClick完全没有关联,所以testClick自然不会被调用。
解决思路一:让组件接收点击回调props(推荐)
这种方式能提升组件的可复用性和可测试性,修改Products.js,让组件接收外部传入的点击回调:
import React, { useState } from 'react'; import { fetchProducts } from '../service'; // 接收onClick回调作为props const Products = ({ onClick }) => { const [product, setProduct] = useState(''); const handleSubmit = (event) => { event.preventDefault(); fetchProducts(); }; return ( <form onSubmit={handleSubmit}> <label> <input type="text" value={product} onChange={(e) => setProduct(e.target.value)} /> </label> <input type="submit" onClick={onClick} // 使用传入的外部回调 data-testid="button" label="Search" /> </form> ); }; export default Products;
然后修改测试代码里的组件传参,把click改成onClick:
test('Button click triggers click event', () => { const testClick = jest.fn(); const { getByTestId } = render(<Products onClick={testClick} />); const button = getByTestId('button'); fireEvent.click(button); expect(testClick).toBeCalled(); });
解决思路二:直接验证组件内部行为(适合不需要复用的场景)
如果不想修改组件结构,可以直接测试点击按钮后触发的内部逻辑,比如验证控制台输出或者submit事件对应的函数调用:
import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import Products from './Products'; import { fetchProducts } from '../service'; // Mock service里的fetchProducts函数 jest.mock('../service', () => ({ fetchProducts: jest.fn() })); test('Button click triggers expected logic', () => { // 监听console.log const logSpy = jest.spyOn(console, 'log').mockImplementation(); const { getByTestId } = render(<Products />); const button = getByTestId('button'); fireEvent.click(button); // 验证console.log输出了'clicked!' expect(logSpy).toHaveBeenCalledWith('clicked!'); // 验证submit事件触发了fetchProducts expect(fetchProducts).toHaveBeenCalled(); // 恢复console.log的原始行为 logSpy.mockRestore(); });
内容的提问来源于stack exchange,提问作者wallwalker
相关产品推荐
相关产品推荐

