Reactjs 如何测试依赖于useContext钩子的react组件?
我有一个使用Reactjs 如何测试依赖于useContext钩子的react组件?,reactjs,jestjs,react-hooks,react-test-renderer,Reactjs,Jestjs,React Hooks,React Test Renderer,我有一个使用useContext的组件,然后它的输出取决于上下文中的值。一个简单的例子: import React, { useContext } from 'react'; const MyComponent = () => { const name = useContext(NameContext); return <div>{name}</div>; }; import React,{useContext}来自“React”; 常量MyCompo
useContext
的组件,然后它的输出取决于上下文中的值。一个简单的例子:
import React, { useContext } from 'react';
const MyComponent = () => {
const name = useContext(NameContext);
return <div>{name}</div>;
};
import React,{useContext}来自“React”;
常量MyComponent=()=>{
const name=useContext(NameContext);
返回{name};
};
使用react和jest快照中的浅层渲染器测试此组件时。如何更改
NameContext
的值?一般来说,使用钩子不会对测试策略有太大的改变。这里更大的问题实际上不是钩子,而是上下文的使用,这使事情变得复杂了一点
有很多方法可以实现这一点,但我发现使用'react-test-renderer/shall'
的唯一方法是注入一个模拟钩子:
import ShallowRenderer from 'react-test-renderer/shallow';
let realUseContext;
let useContextMock;
// Setup mock
beforeEach(() => {
realUseContext = React.useContext;
useContextMock = React.useContext = jest.fn();
});
// Cleanup mock
afterEach(() => {
React.useContext = realUseContext;
});
test("mock hook", () => {
useContextMock.mockReturnValue("Test Value");
const element = new ShallowRenderer().render(
<MyComponent />
);
expect(element.props.children).toBe('Test Value');
});
使用ReactDOM
最后,有一个使用ReactDOM
测试钩子的示例,它也可以工作。当然,使用ReactDOM
意味着这也是一种深度渲染,而不是浅渲染
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
test("with ReactDOM", () => {
act(() => {
ReactDOM.render((
<NameContext.Provider value="Provided Value">
<MyComponent />
</NameContext.Provider>
), container);
});
expect(container.textContent).toBe("Provided Value");
});
let容器;
在每个之前(()=>{
container=document.createElement('div');
文件.正文.附件(容器);
});
之后(()=>{
文件.正文.删除文件(容器);
container=null;
});
测试(“使用ReactDOM)”,()=>{
行动(()=>{
ReactDOM.render((
),货柜);
});
expect(container.textContent).toBe(“提供的值”);
});
我尝试使用Enzyme+.dive
,但潜水时,它无法识别上下文道具,而是获得默认道具。事实上,这是酶研究小组已知的问题。
同时,我提出了一个更简单的解决方案,其中包括创建一个自定义钩子,只返回useContext
,并在测试中模拟该自定义钩子的返回:
AppContext.js-创建上下文
import React, { useContext } from 'react';
export const useAppContext = () => useContext(AppContext);
const defaultValues = { color: 'green' };
const AppContext = React.createContext(defaultValues);
export default AppContext;
App.js — 提供背景
import React from 'react';
import AppContext from './AppContext';
import Hello from './Hello';
export default function App() {
return (
<AppContext.Provider value={{ color: 'red' }}>
<Hello />
</AppContext.Provider>
);
}
import React from 'react';
import { useAppContext } from './AppContext';
const Hello = props => {
const { color } = useAppContext();
return <h1 style={{ color: color }}>Hello {color}!</h1>;
};
export default Hello;
import { mount } from 'enzyme';
import NameContext from './NameContext';
test("non-shallow render", () => {
const dummyValue = {
name: 'abcd',
customizeName: jest.fn(),
...
};
const wrapper = mount(
<NameContext.Provider value={dummyValue}>
<MyComponent />
</NameContext.Provider>
);
// then use
wrapper.find('...').simulate('change', ...);
...
expect(wrapper.find('...')).to...;
});
从“React”导入React;
从“./AppContext”导入AppContext;
从“./Hello”导入Hello;
导出默认函数App(){
返回(
或者,如果您在隔离测试组件时没有安装父组件,您可以简单地模拟useContext:
jest.mock('react',()=>{
const ActualReact=jest.requireActual('react'))
返回{
…实际反应,
useContext:()=>({}),//激发useContext时要返回的内容将转到此处
}
})
您仍然可以使用动态useContext
值和全局jest.fn
来完成上述可接受的答案,对于非浅层渲染,我稍微调整了代码,使其仅用上下文包围我的组件
import React from 'react';
import AppContext from './AppContext';
import Hello from './Hello';
export default function App() {
return (
<AppContext.Provider value={{ color: 'red' }}>
<Hello />
</AppContext.Provider>
);
}
import React from 'react';
import { useAppContext } from './AppContext';
const Hello = props => {
const { color } = useAppContext();
return <h1 style={{ color: color }}>Hello {color}!</h1>;
};
export default Hello;
import { mount } from 'enzyme';
import NameContext from './NameContext';
test("non-shallow render", () => {
const dummyValue = {
name: 'abcd',
customizeName: jest.fn(),
...
};
const wrapper = mount(
<NameContext.Provider value={dummyValue}>
<MyComponent />
</NameContext.Provider>
);
// then use
wrapper.find('...').simulate('change', ...);
...
expect(wrapper.find('...')).to...;
});
从“酶”导入{mount};
从“./NameContext”导入NameContext;
测试(“非浅层渲染”,()=>{
常量dummyValue={
名称:“abcd”,
customizeName:jest.fn(),
...
};
常量包装器=装入(
);
//然后使用
find(“…”).simulate('change',…);
...
期望(wrapper.find(“…”).to。。。;
});
以前的帖子,但如果它对某人有帮助,我就是这样让它发挥作用的
import*as React from'React';
从“酶”导入{shall};
描述('MyComponent',()=>{
它('应该使用上下文模拟和浅层渲染div标记',()=>{
jest.spyOn(React,'useContext').mockImplementation(()=>({
名称:“这是一个模拟上下文返回值”
}));
常量myComponent=浅(
).潜水();
expect(myComponent.toMatchSnapShot();
});
});
我做的是测试是否使用了useContext
。在我的例子中,useContext
返回名为dispatch
的函数
在组件中,我有:
const dispatch = useContext(...);
然后在onChange
方法中:
dispatch({ type: 'edit', payload: { value: e.target.value, name: e.target.name } });
所以一开始的内部测试:
const dispatch = jest.fn();
React.useContext = (() => dispatch) as <T>(context: React.Context<T>) => T;
你可以用
包装你的组件:这似乎不适合浅层渲染,因为这样它就不会渲染我的组件的内部。当我尝试这样做时,我得到了这样的快照:
而不是Paul
是的,这里是为.dive()制作的
来自enzym,但react-shallow渲染器没有类似的功能。是的,你是对的,错过了模拟的想法。我没有想到这一点。没有其他方法那么优雅,但比我尝试的方法更优雅。我喜欢deep
函数的想法,我希望他们能在s将其引入react-shallow渲染器有一点。谢谢你的回答!我在使用Ezyme时面临着一个挑战,我完全遵循了你展示的模拟想法,但问题是当我检查我的条件组件是否使用上下文值呈现时。注意:我使用useEffect中的上下文值更新我的组件。我遵循了相同的示例。我看到实际的默认值是moc每次我运行测试时都会被忽略。模拟值未渲染,请告诉我缺少什么。接受的答案包含非浅层渲染的示例。如果您仔细查看,它不完全相同(使用TestRenderer)当我尝试它时,给了我一个错误。因此我共享了它。我实现了它,但它对我不起作用。包装器
返回为未定义=/Warning:它到处替换useContext
,因此破坏了测试中也使用useContext
的任何部分,例如样式化组件。Alex Andrade的回答是我将允许您分离出您想要模仿的useContext
的哪些用法。@Chris您到底听说过jest吗?@Chris您可以检查值。displayName
查看它是否来自StylesContext
,如果您有useContext:value=>
,并相应返回或保留原始函数。
it('calls function when change address input', () => {
const input = component.find('[name="address"]');
input.simulate('change', { target: { value: '123', name: 'address' } });
expect(dispatch).toHaveBeenCalledTimes(1);
});