问题
上面代码的一个基本问题是,无法断言上下文函数是
成功地
/
弱点
被称为。现在,你点击了一个按钮,但没有任何迹象表明发生了什么
之后
单击按钮(上下文/组件中没有任何内容被更改/更新,以反映任何类型的UI更改)。因此,如果没有上下文函数,那么断言调用了上下文函数是没有好处的
后果
点击按钮。
除上述内容外,酶适配器不支持使用
createContext
方法
然而,有一个解决这个限制的办法!您不需要对组件进行单元测试,而是需要创建一个与上下文的集成测试。您将对上下文函数进行断言,而不是断言调用了上下文函数
后果
点击改变上下文的按钮,以及它对组件的影响。
解决方案
由于组件与上下文中的内容相关联,因此您将创建一个集成测试。例如,您将在测试中使用上下文包装组件,并对结果进行断言:
import * as React from "react";
import { mount } from "enzyme";
import Component from "./path/to/Component";
import ContextProvider from "./path/to/ContextProvider";
const wrapper = mount(
<ContextProvider>
<Component />
</ContextProvider>
);
it("updates the UI when the button is clicked", () => {
wrapper.find("button").simulate("click");
expect(wrapper.find(...)).toEqual(...);
})
通过执行上述操作,您可以在
Component
.此外,通过使用
mount
,你不必
dive
进入
ContextProvider
查看
组成部分
加成
演示示例
本演示利用上下文将主题从“亮”切换到“暗”,反之亦然。点击
Tests
选项卡来运行
App.test.js
集成测试。
代码示例
应用程序。js
import * as React from "react";
import { ThemeContext } from "./ThemeProvider";
import "./styles.css";
class App extends React.PureComponent {
render() {
const { theme, toggleTheme } = this.context;
return (
<div className="app">
<h1>Current Theme</h1>
<h2 data-testid="theme" className={`${theme}-text`}>
{theme}
</h2>
<button
className={`${theme}-button button`}
data-testid="change-theme-button"
type="button"
onClick={toggleTheme}
>
Change Theme
</button>
</div>
);
}
}
App.contextType = ThemeContext;
export default App;
这是我的提供者。js
import * as React from "react";
export const ThemeContext = React.createContext();
class ThemeProvider extends React.Component {
state = {
theme: "light"
};
toggleTheme = () => {
this.setState((prevState) => ({
theme: prevState.theme === "light" ? "dark" : "light"
}));
};
render = () => (
<ThemeContext.Provider
value={{ theme: this.state.theme, toggleTheme: this.toggleTheme }}
>
{this.props.children}
</ThemeContext.Provider>
);
}
export default ThemeProvider;
指数js
import * as React from "react";
import ReactDOM from "react-dom";
import ThemeProvider from "./ThemeProvider";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<ThemeProvider>
<App />
</ThemeProvider>
</React.StrictMode>,
document.getElementById("root")
);
测试示例
这是一个如何对照上面的演示示例进行测试的示例。
主题。js
(可选的可重用测试工厂函数,用于使用上下文包装组件——在您可能需要调用
wrapper.setProps()
在根目录上更新组件的道具)
import * as React from "react";
import { mount } from "enzyme";
import ThemeProvider from "./ThemeProvider";
/**
* Factory function to create a mounted wrapper with context for a React component
*
* @param Component - Component to be mounted
* @param options - Optional options for enzyme's mount function.
* @function createElement - Creates a wrapper around passed in component with incoming props (now we can use wrapper.setProps on root)
* @returns ReactWrapper - a mounted React component with context.
*/
export const withTheme = (Component, options = {}) =>
mount(
React.createElement((props) => (
<ThemeProvider>{React.cloneElement(Component, props)}</ThemeProvider>
)),
options
);
export default withTheme;
应用程序。测验js
import * as React from "react";
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import withTheme from "./withTheme";
import App from "./App";
configure({ adapter: new Adapter() });
// wrapping "App" with some context
const wrapper = withTheme(<App />);
/*
THIS "findByTestId" FUNCTION IS OPTIONAL!
I'm using "data-testid" attributes, since they're static properties in
the DOM that are easier to find within a "wrapper".
This is 100% optional, but easier to use when a "className" may be
dynamic -- such as when using css modules that create dynamic class names.
*/
const findByTestId = (id) => wrapper.find(`[data-testid='${id}']`);
describe("App", () => {
it("initially displays a light theme", () => {
expect(findByTestId("theme").text()).toEqual("light");
expect(findByTestId("theme").prop("className")).toEqual("light-text");
expect(findByTestId("change-theme-button").prop("className")).toContain(
"light-button"
);
});
it("clicking on the 'Change Theme' button toggles the theme between 'light' and 'dark'", () => {
// change theme to "dark"
findByTestId("change-theme-button").simulate("click");
expect(findByTestId("theme").text()).toEqual("dark");
expect(findByTestId("theme").prop("className")).toEqual("dark-text");
expect(findByTestId("change-theme-button").prop("className")).toContain(
"dark-button"
);
// change theme to "light"
findByTestId("change-theme-button").simulate("click");
expect(findByTestId("theme").text()).toEqual("light");
});
});