我刚开始测试React,我试图测试当用户更改AsyncSelect组件(由onInputChange回调连接)上的输入值时,是否调用了HotelSelect.handleInputChange。
HotelSelect.js
import React, { useEffect, useState } from 'react';
import HotelSearchApi from '../api/HotelSearchApi';
import AsyncSelect from 'react-select/async';
import './HotelSelect.css';
export default function HotelSelect({
...props
}) {
const [searchTerm, setSearchTerm] = useState('');
const [selectValue, setSelectValue] = useState({value: '', label: ''});
useEffect(() => {
setSelectValue({value: props.hotel.brand_code, label: props.hotel.brand_code});
}, [props.hotel])
function formatHotelList(hotels) {
return hotels.map(function(hotel) {
return {...hotel, ...{ value: hotel.brand_code, label: hotel.brand_code } }
});
}
function handleInputChange(newTerm) {
setSearchTerm(newTerm);
}
async function loadOptions() {
let hotels = await HotelSearchApi.search(searchTerm);
return formatHotelList(hotels);
}
return (
<AsyncSelect cacheOptions={true}
className="HotelSelect"
defaultInputValue={props.hotel.brand_code}
value={selectValue}
loadOptions={loadOptions}
onChange={props.changeHotel}
onInputChange={handleInputChange}/>
)
};
HotelSelect.test.js
import React from 'react';
import HotelSelect from './HotelSelect';
import AsyncSelect from 'react-select/async';
import renderer from 'react-test-renderer';
import { mount, shallow, render } from 'enzyme';
const hotel = {
brand_code: '12345'
}
const searchData = {
data: [
{
id: '123',
type: 'hotel',
attributes: {
brand_code: '55555',
name: 'A Hotel'
}
}
]
}
it('renders correctly', () => {
const selector = renderer
.create(<HotelSelect hotel={hotel} />)
.toJSON();
expect(selector).toMatchSnapshot();
});
it('should update searchTerm as user changes input', () => {
const myF = jest.fn();
const selector = mount(<HotelSelect hotel={hotel} />);
selector.handleInputChange = myF;
let input = selector.find('input');
input.simulate('change', { target: { value: '5' } });
expect(myF).toHaveBeenCalled();
});
当我使用console.log检查选择器时,看起来好像附加了模拟功能:
● Console
console.log src/components/HotelSelect.test.js:38
ReactWrapper {
handleInputChange: [Function: mockConstructor] {
_isMockFunction: true,
getMockImplementation: [Function],
mock: [Getter/Setter],
mockClear: [Function],
mockReset: [Function],
mockRestore: [Function],
mockReturnValueOnce: [Function],
mockResolvedValueOnce: [Function],
mockRejectedValueOnce: [Function],
mockReturnValue: [Function],
mockResolvedValue: [Function],
mockRejectedValue: [Function],
mockImplementationOnce: [Function],
mockImplementation: [Function],
mockReturnThis: [Function],
mockName: [Function],
getMockName: [Function]
}
}
不幸的是,该函数似乎没有被触发:
● should update searchTerm as user changes input
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
37 | input.simulate('change', { target: { value: '5' } });
38 | console.log(selector);
> 39 | expect(myF).toHaveBeenCalled();
| ^
40 | });
41 |
at Object.<anonymous> (src/components/HotelSelect.test.js:39:15)
我一定不能在输入框上触发onChange事件,而应该在AsyncSelect组件上触发onInputChange回调。
我在网上阅读了一些TDD帖子,有趣的文档以及关于Stackoverflow的至少3个问题,但是我仍然无法弄清楚。
感谢任何帮助!
更新: 我将测试更改为以下内容:
import React from 'react';
import HotelSearchApi from '../api/HotelSearchApi';
import HotelSelect from './HotelSelect';
import AsyncSelect from 'react-select/async';
import renderer from 'react-test-renderer';
import { mount, shallow, render } from 'enzyme';
const hotel = {
brand_code: '12345'
}
const searchData = {
data: [
{
id: '123',
type: 'hotel',
attributes: {
brand_code: '55555',
name: 'A Hotel'
}
}
]
}
jest.mock('../api/HotelSearchApi');
it('renders correctly', () => {
const selector = renderer
.create(<HotelSelect hotel={hotel} />)
.toJSON();
expect(selector).toMatchSnapshot();
});
it('should update searchTerm as user changes input', () => {
const selector = mount(<HotelSelect hotel={hotel} />);
let input = selector.find('input');
expect(HotelSearchApi.search).not.toHaveBeenCalled();
input.simulate('change', { target: { value: '5' } });
expect(HotelSearchApi.search).toHaveBeenCalledWith('5');
});
但是change事件似乎并没有更新输入中的值。测试失败的消息表明它正在接收'12345',这是初始值(hotel.brand_code):
● should update searchTerm as user changes input
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: "5"
Received: "12345"
Number of calls: 1
39 | input.simulate('change', { target: { value: '5' } });
40 |
> 41 | expect(HotelSearchApi.search).toHaveBeenCalledWith('5');
| ^
42 | });
43 |
at Object.<anonymous> (src/components/HotelSelect.test.js:41:33)
答案 0 :(得分:1)
问题与
selector.handleInputChange = myF;
实际上,您不能以此方式模拟某些内部变量。如果这是基于类的组件中的方法,则可以按照
HotelSelect.instance().handleInputChange = jest.fn();
但是无论如何这将是一个糟糕的举动。你知道吗?您实际上不需要模拟内部方法。
检查已调用的内部方法没有任何价值。您只需要确保某些外部API已被最终调用并具有预期的参数即可:
import HotelSearchApi from '../api/HotelSearchApi';
jest.mock('../api/HotelSearchApi'); // automock
...
it('should update searchTerm as user changes input', () => {
const selector = mount(<HotelSelect hotel={hotel} />);
let input = selector.find('input');
expect(HotelSearchApi.search).not.toHaveBeenCalled();
input.simulate('change', { target: { value: '5' } });
expect(HotelSearchApi.search).toHaveBeenCalledWith('5');
});