使用Enzyme模拟React函数组件输入变更时测试失败求助
Enzyme测试React输入组件时,模拟change事件后状态未更新导致断言失败
我在测试一个React的Join组件时遇到了问题,组件里有两个输入框,绑定了state和change事件,但测试时模拟name输入框的change事件后,值始终是空字符串,断言失败。
组件代码:
import React, { useState } from 'react'; import { BrowserRouter as Router, Link } from 'react-router-dom'; import styles from './Join.module.scss'; const Join = () => { const [name, setName] = useState(''); const [room, setRoom] = useState(''); const handleChange = (e) => { if (e.target.name === 'name') { setName(e.target.value); } else if (e.target.name === 'room') { setRoom(e.target.value); } }; return ( <section className={styles.main}> <h1 className={styles.main__title}>Join</h1> <form className={styles.form}> <label className={styles.form__label}>Name</label> <input className={styles.form__input} name="name" type="text" onChange={handleChange} /> <label className={styles.form__label}>Room</label> <input className={styles.form__input} name="room" type="text" onChange={handleChange} /> </form> <Router> <Link onClick={(e) => (!name || !room ? e.preventDefault() : null)} to={`/chat?name=${name}&room=${room}`} > <button className="btn btn--submit" type="submit"> Sign In </button> </Link> </Router> </section> ); }; export default Join;
我的测试代码:
import React from 'react'; import { mount } from 'enzyme'; import Join from '../Join'; describe('<Join />', () => { let wrapper; beforeEach(() => { wrapper = mount(<Join />); }); test('1- renders without errors and matches h1', () => { const title = wrapper.find('h1'); expect(title.text()).toBe('Join'); }); test('2- name input', () => { const name = wrapper.find('[name="name"]'); console.log(name.debug()); name.simulate('change', { target: { value: 'foo' }, }); expect(name.text()).toBe('foo'); }); });
请问我哪里操作有误?希望能得到帮助。
问题分析与修复方案
我看到你的问题了,主要有两个关键错误,咱们一步步来修正:
1. 模拟change事件时缺少name属性
你的组件里的handleChange函数是通过判断e.target.name来决定更新name还是room状态的,但你在模拟事件时只传递了value,没有传递name: 'name'。这会导致handleChange里的条件不匹配,根本不会触发setName,所以state里的name还是初始空值。
2. 断言input值的方式错误
name.text()获取的是input标签内部的文本内容,但input的实际值是存在value属性里的,你应该用name.props().value来获取当前输入框的value,或者直接断言组件的state值(这种方式更准确,能直接验证状态是否真的更新了)。
修正后的测试代码
import React from 'react'; import { mount } from 'enzyme'; import Join from '../Join'; describe('<Join />', () => { let wrapper; beforeEach(() => { wrapper = mount(<Join />); }); test('1- renders without errors and matches h1', () => { const title = wrapper.find('h1'); expect(title.text()).toBe('Join'); }); test('2- name input updates state correctly', () => { const nameInput = wrapper.find('[name="name"]'); // 模拟change事件,必须包含name和value,匹配组件的handleChange逻辑 nameInput.simulate('change', { target: { name: 'name', value: 'foo' } }); // 方式1:断言输入框的value属性 expect(nameInput.props().value).toBe('foo'); // 方式2:直接断言组件的state(更准确,验证state是否更新) expect(wrapper.find(Join).state('name')).toBe('foo'); }); });
另外提个小优化点:你的组件里把Router放在了Join组件内部,测试时可以用MemoryRouter来包裹组件,避免每次mount都创建新的BrowserRouter,但这不是导致当前测试失败的原因,只是让测试更规范的小建议。
内容的提问来源于stack exchange,提问作者Mugg84
相关产品推荐
相关产品推荐

