Reactjs 在测试期间模拟表单提交时,不会调用在Redux连接组件中作为道具传递的操作
我正在测试我的第一个应用程序,在测试Redux连接的组件时遇到了问题 更具体地说,我正在测试Reactjs 在测试期间模拟表单提交时,不会调用在Redux连接组件中作为道具传递的操作,reactjs,unit-testing,react-redux,jestjs,enzyme,Reactjs,Unit Testing,React Redux,Jestjs,Enzyme,我正在测试我的第一个应用程序,在测试Redux连接的组件时遇到了问题 更具体地说,我正在测试Search.js。这个想法是在子组件DisplaySearcgBar.js中模拟表单提交,然后测试是否调用了setAlert和getRestaurants 在测试#3中,由于提交表单时输入是空的,Search.js应该调用OnSubmit(),它应该调用setAlert,在测试#4中,它应该调用getRestaurants,因为提供了输入 两项测试均被拒绝,但出现相同错误: Search › 3 - s
Search.js
。这个想法是在子组件DisplaySearcgBar.js
中模拟表单提交,然后测试是否调用了setAlert
和getRestaurants
在测试#3中,由于提交表单时输入是空的,Search.js
应该调用OnSubmit()
,它应该调用setAlert
,在测试#4中,它应该调用getRestaurants
,因为提供了输入
两项测试均被拒绝,但出现相同错误:
Search › 3 - setAlert called if search button is pressed with no input
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
37 | wrapper.find('[data-test="search"]').simulate('click');
38 | //expect(store.getActions().length).toBe(1);
> 39 | expect(wrapper.props().children.props.props.setAlert).toHaveBeenCalled();
| ^
40 | });
41 |
42 | test('4 - getRestaurant called when inputs filled and search button clicked ', () => {
at Object.<anonymous> (src/Components/restaurants/Search/__tests__/Search.test.js:39:59)
● Search › 4 - getRestaurant called when inputs filled and search button clicked
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
55 | wrapper.find('[data-test="search"]').simulate('click');
56 |
> 57 | expect(wrapper.props().children.props.props.getRestaurants).toHaveBeenCalled();
| ^
58 | });
59 | });
60 |
at Object.<anonymous> (src/Components/restaurants/Search/__tests__/Search.test.js:57:65)
以下是Github上的完整存储库:
提前感谢您的帮助 这是因为enzyme的
find()
返回html节点的集合
还记得这个老好人的错误吗
方法“simulate”将在1个节点上运行
请这样尝试:wrapper.find(“…”).at(0)
此外,当您希望模拟的“setAlert()和
getRestaurant()被调用时,您引用它们的方式无法让我们知道它是正确的还是错误的引用。因此,请提供相关的
debug()`结果,或者更好地模拟它们:
const mockSetAlert = jest.fn();
const mockGetRestaurants = jest.fn();
const wrapper = mount(
<Search setAlert={mockSetAlert} getRestaurants={mockGetRestaurants} />
);
...
expect(mockSetAlert).toHaveBeenCalled();
expect(mockGetRestaurants).toHaveBeenCalled();
const mockSetAlert=jest.fn();
const mockGetRestaurants=jest.fn();
常量包装器=装入(
);
...
expect(mockSetAlert).toHaveBeenCalled();
期望(mockGetRestaurants).tohavebeincall();
这是一个简化的示例,但您可以理解…这是因为enzyme的
find()
返回html节点的集合
还记得这个老好人的错误吗
方法“simulate”将在1个节点上运行
请这样尝试:wrapper.find(“…”).at(0)
此外,当您希望模拟的“setAlert()和
getRestaurant()被调用时,您引用它们的方式无法让我们知道它是正确的还是错误的引用。因此,请提供相关的
debug()`结果,或者更好地模拟它们:
const mockSetAlert = jest.fn();
const mockGetRestaurants = jest.fn();
const wrapper = mount(
<Search setAlert={mockSetAlert} getRestaurants={mockGetRestaurants} />
);
...
expect(mockSetAlert).toHaveBeenCalled();
expect(mockGetRestaurants).toHaveBeenCalled();
const mockSetAlert=jest.fn();
const mockGetRestaurants=jest.fn();
常量包装器=装入(
);
...
expect(mockSetAlert).toHaveBeenCalled();
期望(mockGetRestaurants).tohavebeincall();
这是一个简化的示例,但您知道……Search.js是一个连接的组件。它的道具是通过mapDispatchToProps从商店里买来的。即使您模拟这些道具,生成的包装器也会从提供者的存储中获取相应的函数。因此,解决方案是检查是否已使用所需类型和负载调用了操作 test-4中的另一个问题是您没有在
事件中传递名称
。因此,未在状态中设置值。要避免这种情况,请使用控制台调试测试
import React from 'react';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import Search from './../Search';
import DisplaySearchBar from '../../../layout/DisplaySearchBar/DisplaySearchBar';
import {
SET_LOADING,
SET_ALERT,
} from '../../../../actions/types';
const mockStore = configureStore([thunk]);
const initialState = {
restaurants: { restaurants: ['foo'], alert: null },
};
const store = mockStore(initialState);
const mockSetAlert = jest.fn();
const mockGetRestaurants = jest.fn();
const wrapper = mount(
<Provider store={store}>
<Search setAlert={mockSetAlert} getRestaurants={mockGetRestaurants} />
</Provider>
);
describe('Search', () => {
afterEach(() => {
jest.clearAllMocks();
});
test('1 - renders without errors', () => {
expect(wrapper.find(DisplaySearchBar)).toHaveLength(1);
});
test('2 - if restaurants clearButton is rendered', () => {
expect(wrapper.find('[data-test="clear"]')).toBeTruthy();
});
test('3 - setAlert called if search button is pressed with no input', () => {
wrapper.find('form').simulate('submit', { preventDefault: () => {} });
const actions= store.getActions();
const expected={
type: SET_ALERT,
payload: expect.objectContaining({msg:"Please fill all the inputs"})
};
expect(actions[0]).toMatchObject(expected);
});
test('4 - getRestaurant called when inputs filled and search button clicked ', () => {
wrapper
.find('[name="where"]')
.at(0)
.simulate('change', { target: { value: 'foo', name:"where" } });
wrapper
.find('[name="what"]')
.at(0)
.simulate('change', { target: { value: 'foo',name:"what" } });
wrapper
.find('[data-test="best_match"]')
.at(0)
.simulate('click');
wrapper.find('form').simulate('submit', { preventDefault: () => {} });
const actions= store.getActions();
const expected={
type: SET_LOADING,
};
expect(actions).toContainEqual(expected);
});
});
从“React”导入React;
从“酶”导入{mount};
从“redux模拟存储”导入configureStore;
从'react redux'导入{Provider};
从“redux thunk”导入thunk;
从“/../Search”导入搜索;
从“../../../layout/DisplaySearchBar/DisplaySearchBar”导入DisplaySearchBar;
进口{
设置_加载,
设置警报,
}来自“../../../../actions/types”;
const mockStore=configureStore([thunk]);
常量初始状态={
餐厅:{餐厅:['foo'],警报:null},
};
常量存储=模拟存储(初始状态);
const mockSetAlert=jest.fn();
const mockGetRestaurants=jest.fn();
常量包装器=装入(
);
描述('搜索',()=>{
之后(()=>{
开玩笑。clearAllMocks();
});
测试('1-无错误呈现',()=>{
expect(wrapper.find(DisplaySearchBar)).toHaveLength(1);
});
测试('2-如果呈现了clearButton',()=>{
expect(wrapper.find('[data test=“clear”]')).toBeTruthy();
});
测试('3-如果在没有输入的情况下按下搜索按钮,则调用setAlert',()=>{
find('form').simulate('submit',{preventDefault:()=>{});
const actions=store.getActions();
预期常数={
类型:设置警报,
有效负载:expect.objectContaining({msg:“请填写所有输入”})
};
expect(操作[0])。toMatchObject(预期);
});
test('4-getRestaurant在填写输入并单击搜索按钮时调用',()=>{
包装纸
.find('[name=“where”]'))
.at(0)
.simulate('change',{target:{value:'foo',name:'where'});
包装纸
.find('[name=“what”]'))
.at(0)
.simulate('change',{target:{value:'foo',name:'what'});
包装纸
.find(“[data test=“best_match”]”)
.at(0)
.模拟(“点击”);
find('form').simulate('submit',{preventDefault:()=>{});
const actions=store.getActions();
预期常数={
类型:设置加载,
};
expect(动作)。toContainEqual(预期);
});
});
Search.js是一个连接的组件。它的道具是通过mapDispatchToProps从商店里买来的。即使您模拟这些道具,生成的包装器也会从提供者的存储中获取相应的函数。因此,解决方案是检查是否已使用所需类型和负载调用了操作
test-4中的另一个问题是您没有在事件中传递名称
。因此,未在状态中设置值。要避免这种情况,请使用控制台调试测试
import React from 'react';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import Search from './../Search';
import DisplaySearchBar from '../../../layout/DisplaySearchBar/DisplaySearchBar';
import {
SET_LOADING,
SET_ALERT,
} from '../../../../actions/types';
const mockStore = configureStore([thunk]);
const initialState = {
restaurants: { restaurants: ['foo'], alert: null },
};
const store = mockStore(initialState);
const mockSetAlert = jest.fn();
const mockGetRestaurants = jest.fn();
const wrapper = mount(
<Provider store={store}>
<Search setAlert={mockSetAlert} getRestaurants={mockGetRestaurants} />
</Provider>
);
describe('Search', () => {
afterEach(() => {
jest.clearAllMocks();
});
test('1 - renders without errors', () => {
expect(wrapper.find(DisplaySearchBar)).toHaveLength(1);
});
test('2 - if restaurants clearButton is rendered', () => {
expect(wrapper.find('[data-test="clear"]')).toBeTruthy();
});
test('3 - setAlert called if search button is pressed with no input', () => {
wrapper.find('form').simulate('submit', { preventDefault: () => {} });
const actions= store.getActions();
const expected={
type: SET_ALERT,
payload: expect.objectContaining({msg:"Please fill all the inputs"})
};
expect(actions[0]).toMatchObject(expected);
});
test('4 - getRestaurant called when inputs filled and search button clicked ', () => {
wrapper
.find('[name="where"]')
.at(0)
.simulate('change', { target: { value: 'foo', name:"where" } });
wrapper
.find('[name="what"]')
.at(0)
.simulate('change', { target: { value: 'foo',name:"what" } });
wrapper
.find('[data-test="best_match"]')
.at(0)
.simulate('click');
wrapper.find('form').simulate('submit', { preventDefault: () => {} });
const actions= store.getActions();
const expected={
type: SET_LOADING,
};
expect(actions).toContainEqual(expected);
});
});
从“React”导入React;
从“酶”导入{mount};
从“redux模拟存储”导入configureStore;
从'react redux'导入{Provider};
从“redux thunk”导入thunk;
从“/../Search”导入搜索;
从“../../../layout/DisplaySearchBar/DisplaySearchBar”导入DisplaySearchBar;
进口{
设置_加载,
设置警报,
}来自“../../../../actions/types”;
const mockStore=configureStore([thunk]);
常量初始状态={
餐厅:{餐厅:['foo'],警报:null}
const mockSetAlert = jest.fn();
const mockGetRestaurants = jest.fn();
const wrapper = mount(
<Search setAlert={mockSetAlert} getRestaurants={mockGetRestaurants} />
);
...
expect(mockSetAlert).toHaveBeenCalled();
expect(mockGetRestaurants).toHaveBeenCalled();
import React from 'react';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import Search from './../Search';
import DisplaySearchBar from '../../../layout/DisplaySearchBar/DisplaySearchBar';
import {
SET_LOADING,
SET_ALERT,
} from '../../../../actions/types';
const mockStore = configureStore([thunk]);
const initialState = {
restaurants: { restaurants: ['foo'], alert: null },
};
const store = mockStore(initialState);
const mockSetAlert = jest.fn();
const mockGetRestaurants = jest.fn();
const wrapper = mount(
<Provider store={store}>
<Search setAlert={mockSetAlert} getRestaurants={mockGetRestaurants} />
</Provider>
);
describe('Search', () => {
afterEach(() => {
jest.clearAllMocks();
});
test('1 - renders without errors', () => {
expect(wrapper.find(DisplaySearchBar)).toHaveLength(1);
});
test('2 - if restaurants clearButton is rendered', () => {
expect(wrapper.find('[data-test="clear"]')).toBeTruthy();
});
test('3 - setAlert called if search button is pressed with no input', () => {
wrapper.find('form').simulate('submit', { preventDefault: () => {} });
const actions= store.getActions();
const expected={
type: SET_ALERT,
payload: expect.objectContaining({msg:"Please fill all the inputs"})
};
expect(actions[0]).toMatchObject(expected);
});
test('4 - getRestaurant called when inputs filled and search button clicked ', () => {
wrapper
.find('[name="where"]')
.at(0)
.simulate('change', { target: { value: 'foo', name:"where" } });
wrapper
.find('[name="what"]')
.at(0)
.simulate('change', { target: { value: 'foo',name:"what" } });
wrapper
.find('[data-test="best_match"]')
.at(0)
.simulate('click');
wrapper.find('form').simulate('submit', { preventDefault: () => {} });
const actions= store.getActions();
const expected={
type: SET_LOADING,
};
expect(actions).toContainEqual(expected);
});
});
// previous code
export const Search = ({ getRestaurants, setAlert }) => {
// rest of the code
import React from 'react';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { Search as BaseSearch } from './../Search';
import { DisplaySearchBar as BaseDisplaySearchBar } from '../../../layout/DisplaySearchBar/DisplaySearchBar';
const mockStore = configureStore([thunk]);
const initialState = {
restaurants: { restaurants: ['foo'], alert: null },
};
const getRestaurants = jest.fn();
const setAlert = jest.fn();
let wrapper, store;
describe('Search', () => {
beforeEach(() => {
store = mockStore(initialState);
wrapper = mount(
<Provider store={store}>
<BaseSearch setAlert={setAlert} getRestaurants={getRestaurants} />
</Provider>
);
});
afterEach(() => {
jest.clearAllMocks();
});
test('1 - renders without errors', () => {
expect(wrapper.find(BaseDisplaySearchBar)).toHaveLength(1);
});
test('2 - if restaurants clearButton is rendered', () => {
expect(wrapper.find('[data-test="clear"]')).toBeTruthy();
});
test('3 - setAlert called if search button is pressed with no input', () => {
wrapper.find('form').simulate('submit', { preventDefault: () => {} });
expect(setAlert).toHaveBeenCalled();
});
test('4 - getRestaurants called when inputs filled and search button clicked ', () => {
wrapper
.find('[name="where"]')
.at(0)
.simulate('change', { target: { value: 'foo', name: 'where' } });
wrapper
.find('[name="what"]')
.at(0)
.simulate('change', { target: { value: 'foo', name: 'what' } });
wrapper
.find('[data-test="best_match"]')
.at(0)
.simulate('click');
wrapper.find('form').simulate('submit', { preventDefault: () => {} });
expect(getRestaurants).toHaveBeenCalled();
});
});