React Native日期选择器组件:Jest测试/模拟moment.js求助
测试React Native中基于moment.js的日期选择器组件
我之前在React Native项目里用moment.js做日期选择器时,也踩过测试模拟的坑,给你分享几个实用的思路和示例,应该能帮你顺利写好测试用例!
一、先搞定moment.js的基础模拟
不管是单元测试还是集成测试,固定moment的时间是关键——不然每次跑测试的时间不一样,结果也会乱。常用的有两种方式:
1. 全局统一mock(适合大部分测试)
在你的测试入口文件(比如jest.setup.js)或者单个测试文件顶部,这么配置:
jest.mock('moment', () => { // 先拿到真实的moment模块,避免完全mock掉所有功能 const actualMoment = jest.requireActual('moment'); // 固定当前时间为2024-05-20,你可以改成自己需要的测试基准时间 actualMoment.now = () => new Date('2024-05-20').valueOf(); return actualMoment; });
这样所有测试里的moment()都会默认返回这个固定时间,测试结果就稳定了。
2. 单个测试用例单独mock
如果某个测试需要特殊时间,就在用例里临时覆盖:
test('测试选择"明天"的逻辑', () => { // 临时把当前时间改成2024-05-20 const mockNow = jest.spyOn(moment, 'now').mockReturnValue(new Date('2024-05-20').valueOf()); // 这里写你的测试逻辑:渲染组件、模拟操作、断言... // 测试完记得恢复,别影响其他用例 mockNow.mockRestore(); });
二、常见日期函数的测试示例
虽然你没贴具体的两个函数,但我举两个最常用的场景,你可以套到自己的代码上:
示例1:测试日期格式化函数(比如formatDisplayDate)
假设这个函数接收moment对象,返回指定格式的字符串:
import moment from 'moment'; import { formatDisplayDate } from '../your-date-picker-component'; describe('formatDisplayDate', () => { it('能把moment对象转成"YYYY-MM-DD"格式', () => { const testDate = moment('2024-05-20'); expect(formatDisplayDate(testDate)).toBe('2024-05-20'); }); it('传入null时返回空字符串', () => { expect(formatDisplayDate(null)).toBe(''); }); // 进阶:验证是否正确调用了moment的format方法 it('调用moment.format时传入了正确的格式参数', () => { const mockFormat = jest.fn(() => '2024-05-20'); // mock moment的实例方法 jest.mock('moment', () => jest.fn(() => ({ format: mockFormat }))); const testDate = new Date('2024-05-20'); formatDisplayDate(testDate); expect(mockFormat).toHaveBeenCalledWith('YYYY-MM-DD'); }); });
示例2:测试日期范围判断函数(比如isDateInRange)
假设这个函数判断某个日期是否在[start, end]范围内:
import moment from 'moment'; import { isDateInRange } from '../your-date-picker-component'; describe('isDateInRange', () => { // 每个用例前固定时间 beforeEach(() => { jest.spyOn(moment, 'now').mockReturnValue(new Date('2024-05-20').valueOf()); }); // 每个用例后恢复mock afterEach(() => { jest.spyOn(moment, 'now').mockRestore(); }); it('日期在范围内时返回true', () => { const start = moment('2024-05-15'); const end = moment('2024-05-25'); const testDate = moment('2024-05-20'); expect(isDateInRange(testDate, start, end)).toBe(true); }); it('日期等于开始/结束日期时返回true', () => { const start = moment('2024-05-20'); const end = moment('2024-05-20'); const testDate = moment('2024-05-20'); expect(isDateInRange(testDate, start, end)).toBe(true); }); it('日期超出范围时返回false', () => { const start = moment('2024-05-10'); const end = moment('2024-05-19'); const testDate = moment('2024-05-20'); expect(isDateInRange(testDate, start, end)).toBe(false); }); });
三、React Native组件的集成测试技巧
如果要测试整个日期选择器的交互(比如点击选择日期后,组件更新显示),可以结合@testing-library/react-native来模拟用户操作:
import { render, fireEvent } from '@testing-library/react-native'; import moment from 'moment'; import DatePicker from '../DatePicker'; // 全局mock moment,固定基准时间 jest.mock('moment', () => { const actualMoment = jest.requireActual('moment'); actualMoment.now = () => new Date('2024-05-20').valueOf(); return actualMoment; }); test('选择日期后,组件正确更新显示并触发回调', () => { const mockOnChange = jest.fn(); // 渲染组件,传入回调 const { getByTestId, getByText } = render(<DatePicker onChange={mockOnChange} />); // 模拟点击日期输入框,弹出选择面板 fireEvent.press(getByTestId('date-picker-trigger')); // 模拟选择22号这个日期 fireEvent.press(getByText('22')); // 验证回调被调用,传入的是正确的moment对象 expect(mockOnChange).toHaveBeenCalledWith(moment('2024-05-22')); // 验证组件显示的日期正确 expect(getByTestId('selected-date')).toHaveTextContent('2024-05-22'); });
四、踩过的坑要注意
- 别完全mock掉moment:如果直接
jest.mock('moment', () => {}),会把moment所有方法都干掉,很多功能用不了。最好用jest.requireActual保留真实模块,只覆盖需要修改的部分(比如now方法)。 - 时区问题:如果你的组件涉及时区,记得在mock时设置默认时区,比如
moment.tz.setDefault('Asia/Shanghai'),避免测试结果和实际运行不一致。 - 清理mock:用
spyOn做的mock,一定要在测试后用mockRestore()恢复,不然会影响其他测试用例的结果。
内容的提问来源于stack exchange,提问作者StuffedPoblano
相关产品推荐
相关产品推荐

