React 测试库如何断言子属性和 Redux 存储更新?
How React Testing Library can assert children props and Redux store updates?
我刚刚开始探索我的 React 应用程序的单元测试框架。我将我的选择锁定在 @testing-library/react
而不是 Enzyme
,因为它是 React 推荐的,有助于编写更健壮的测试。我还打算使用 Jest
来确保完整性。
但在我继续 React Testing Library
之前,我遇到了几个问题。有人可以帮忙吗?
例如,
- 有一个
Editor
组件可以根据用户输入更新 Redux 存储。
- 还有另一个组件
Preview
直接从存储中读取值。
- 还有第三个组件
Container
,它包含上述两个传递一些道具的组件。
问题:
- 如何测试
Editor
组件中的用户输入是否实际更新商店?
- 如何测试
Preview
组件是否可以呈现从存储中读取的值?
- 如何测试
Container
组件是否正确地将 props 传递给它的子组件?
Container.jsx
import React from 'react';
import Editor from './Editor';
import Preview from './Preview';
const Container = ({editorType}) => {
return (
<div>
<Editor type={editorType} />
<Preview />
</div>
);
};
export default Container;
Editor.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {setName} from './actions';
const Editor = ({type}) => {
const name = useSelector(state => state.name);
const dispatch = useDispatch();
return (
<div>
<h1>Type: {type}</h1>
<input value={name} onChange={evt => dispatch(setName(evt.target.value))} />
</div>
);
};
export default Editor;
Preview.jsx
import React from 'react';
import { useSelector } from 'react-redux';
const Editor = ({type}) => {
const name = useSelector(state => state.name);
return (
<div>
<h1>Name: {name}</h1>
</div>
);
};
export default Preview;
这是我的做法:
如何测试编辑器组件中的用户输入是否实际更新存储?
查看您的代码,Editor 不会更新存储,它不会调用 useDispatch。但假设它确实如此,使用 https://github.com/reduxjs/redux-mock-store 我会为测试中的组件设置一个模拟商店提供者,然后触发用户输入并检查模拟商店是否已调度正确的操作,并使用正确的有效负载:
describe('<Editor />', () => {
let store: MockStoreEnhanced;
let rtl: RenderResult;
beforeEach(() => {
store = mockStore({ /* your store structure */ });
rtl = render(
<Provider store={store}>
<Editor type={...} />
</Provider>
);
});
test('dispatch action correctly', () => {
fireEvent.change(rtl.container.querySelector('input'), { target: {value: 'test' } });
// check actions that were dispatched by change event
// getActions return an array avec redux actions (type, payload)
expect(store.getActions()).toEqual(...);
}
}
除此之外你应该测试你的 store reducer 和 action creators,以确保 action 正确地修改了 store。
如何测试预览组件是否可以呈现从存储中读取的值?
相同的原则,使用模拟商店设置测试,使用相关值初始化该商店,然后测试组件是否显示商店中的内容:
describe('<Preview />', () => {
let store: MockStoreEnhanced;
let rtl: RenderResult;
beforeEach(() => {
store = mockStore({ name: 'This is name from store' });
rtl = render(
<Provider store={store}>
<Preview type={...} />
</Provider>
);
});
test('displays store content correctly', () => {
expect(rtl.getByText('This is name from store')).toBeTruthy();
}
}
如何测试 Container 组件是否正确地将 props 传递给它 children?
在这里你应该一起测试Container + Preview + Editor,并找到一些可视化的东西来测试(作为一个真实的用户会感知它),包括Container传递给它的道具children。
例如,如果预览显示 props.type,它来自 Container editorType props :
describe('<Container />', () => {
// mocked store may not be necessary here, depending on your use case
let store: MockStoreEnhanced;
let rtl: RenderResult;
beforeEach(() => {
store = mockStore({ name: 'This is name from store' });
rtl = render(
<Provider store={store}>
<Container editorType={'toTest'} />
</Provider>
);
});
test('displays store content correctly', () => {
// check that 'toTest' exists in DOM because it is rendered by Preview component, a child of Container
expect(rtl.getByText('toTest')).toBeTruthy();
}
}
总而言之,我的方法是:
- 单独测试动作和缩减器(测试非常简单,状态机,只是简单的 Jest 测试)
- 测试组件是否在正确的用户输入上使用正确的负载发送正确的操作
- 模拟商店为从商店读取数据的组件设置相关用例(初始商店值)
最后 2 项我使用 https://github.com/reduxjs/redux-mock-store。
我刚刚开始探索我的 React 应用程序的单元测试框架。我将我的选择锁定在 @testing-library/react
而不是 Enzyme
,因为它是 React 推荐的,有助于编写更健壮的测试。我还打算使用 Jest
来确保完整性。
但在我继续 React Testing Library
之前,我遇到了几个问题。有人可以帮忙吗?
例如,
- 有一个
Editor
组件可以根据用户输入更新 Redux 存储。 - 还有另一个组件
Preview
直接从存储中读取值。 - 还有第三个组件
Container
,它包含上述两个传递一些道具的组件。
问题:
- 如何测试
Editor
组件中的用户输入是否实际更新商店? - 如何测试
Preview
组件是否可以呈现从存储中读取的值? - 如何测试
Container
组件是否正确地将 props 传递给它的子组件?
Container.jsx
import React from 'react';
import Editor from './Editor';
import Preview from './Preview';
const Container = ({editorType}) => {
return (
<div>
<Editor type={editorType} />
<Preview />
</div>
);
};
export default Container;
Editor.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {setName} from './actions';
const Editor = ({type}) => {
const name = useSelector(state => state.name);
const dispatch = useDispatch();
return (
<div>
<h1>Type: {type}</h1>
<input value={name} onChange={evt => dispatch(setName(evt.target.value))} />
</div>
);
};
export default Editor;
Preview.jsx
import React from 'react';
import { useSelector } from 'react-redux';
const Editor = ({type}) => {
const name = useSelector(state => state.name);
return (
<div>
<h1>Name: {name}</h1>
</div>
);
};
export default Preview;
这是我的做法:
如何测试编辑器组件中的用户输入是否实际更新存储?
查看您的代码,Editor 不会更新存储,它不会调用 useDispatch。但假设它确实如此,使用 https://github.com/reduxjs/redux-mock-store 我会为测试中的组件设置一个模拟商店提供者,然后触发用户输入并检查模拟商店是否已调度正确的操作,并使用正确的有效负载:
describe('<Editor />', () => {
let store: MockStoreEnhanced;
let rtl: RenderResult;
beforeEach(() => {
store = mockStore({ /* your store structure */ });
rtl = render(
<Provider store={store}>
<Editor type={...} />
</Provider>
);
});
test('dispatch action correctly', () => {
fireEvent.change(rtl.container.querySelector('input'), { target: {value: 'test' } });
// check actions that were dispatched by change event
// getActions return an array avec redux actions (type, payload)
expect(store.getActions()).toEqual(...);
}
}
除此之外你应该测试你的 store reducer 和 action creators,以确保 action 正确地修改了 store。
如何测试预览组件是否可以呈现从存储中读取的值?
相同的原则,使用模拟商店设置测试,使用相关值初始化该商店,然后测试组件是否显示商店中的内容:
describe('<Preview />', () => {
let store: MockStoreEnhanced;
let rtl: RenderResult;
beforeEach(() => {
store = mockStore({ name: 'This is name from store' });
rtl = render(
<Provider store={store}>
<Preview type={...} />
</Provider>
);
});
test('displays store content correctly', () => {
expect(rtl.getByText('This is name from store')).toBeTruthy();
}
}
如何测试 Container 组件是否正确地将 props 传递给它 children?
在这里你应该一起测试Container + Preview + Editor,并找到一些可视化的东西来测试(作为一个真实的用户会感知它),包括Container传递给它的道具children。
例如,如果预览显示 props.type,它来自 Container editorType props :
describe('<Container />', () => {
// mocked store may not be necessary here, depending on your use case
let store: MockStoreEnhanced;
let rtl: RenderResult;
beforeEach(() => {
store = mockStore({ name: 'This is name from store' });
rtl = render(
<Provider store={store}>
<Container editorType={'toTest'} />
</Provider>
);
});
test('displays store content correctly', () => {
// check that 'toTest' exists in DOM because it is rendered by Preview component, a child of Container
expect(rtl.getByText('toTest')).toBeTruthy();
}
}
总而言之,我的方法是:
- 单独测试动作和缩减器(测试非常简单,状态机,只是简单的 Jest 测试)
- 测试组件是否在正确的用户输入上使用正确的负载发送正确的操作
- 模拟商店为从商店读取数据的组件设置相关用例(初始商店值)
最后 2 项我使用 https://github.com/reduxjs/redux-mock-store。