我对在React中进行测试非常陌生。我在排序功能的末尾更新状态。我只能弄清楚如何从我正在创建的包装器中获取状态。
test('Sorting Data No Change', () => {
const wrapper = shallow(render(<Table headers={[
{name:"ID", prop: "id"},
{name:"Manufacturer", prop: "manufacturer"},
{name:"Model", prop: "model"}
]}
data={data}
bold ="Ford"
upper="Model" />);
);
console.log(wrapper.instance().sort("asend", "id"));
expect(wrapper.state.filtered).equals(data);
});
答案 0 :(得分:0)
您可以获取更新前后的状态,并像这样检查它们:
test('Sorting Data No Change', () => {
const wrapper = shallow(render(<Table headers={[
{name:"ID", prop: "id"},
{name:"Manufacturer", prop: "manufacturer"},
{name:"Model", prop: "model"}
]}
data={data}
bold ="Ford"
upper="Model" />);
);
let filterState = wrapper.state.filtered;
// update the state
wrapper.instance().sort("asend", "id");
// check the new state equals to the past state
expect(wrapper.state.filtered).equals(filterState);
});
答案 1 :(得分:0)
有几种测试组件的方法。您可以装载父级,模拟点击并针对父级state
和子级props
进行断言。或者,您可以挂载子级,操纵props
以模拟排序单击,并针对prop数据中的更改进行断言。 要做的最终要取决于您和您的工作流程。
也就是说,我喜欢在一个.test.js
文件中测试两个组件,当两个组件固有地链接为一个组件时(意味着,一个组件不能没有另一个组件就无法工作)。因此,在下面的示例中,当state
发生更改时,我将针对父props
进行声明,并对某些子元素进行一些声明。
工作示例(点击Tests
标签以运行测试)
测试中发生了什么(您也可以在Browser
标签中进行操作以了解流程):
id
表标题旁边的“排序”图标上的初始点击data
,sortByName
和sortByType
;然后对孩子的可见元素进行断言(在这种情况下,请确保id
旁边的“排序”图标根据点击顺序更改为向上/向下箭头-第一次单击为{{ 1}},第二次点击是desc
,然后在随后的点击中在两者之间切换。asc
按钮以重置为Clear Filters
。components / App / index.js
initialState
components / App / __ tests __ / App.test.js
import React, { Component } from "react";
import Table from "../Table";
import { app } from "./App.module.scss";
export const initialState = {
data: [
{
id: "1",
manufacturer: "Ford",
model: "Mustang"
},
{
id: "2",
manufacturer: "Toyota",
model: "Tundra"
},
{
id: "3",
manufacturer: "Honda",
model: "Civic"
}
],
headers: ["id", "manufacturer", "model"],
sortByName: "",
sortByType: ""
};
class App extends Component {
state = initialState;
clearFilters = () => this.setState(initialState);
handleSort = (sortByName, sortByType) => {
this.setState(prevState => ({
data: Array.from(prevState.data).sort((a, b) =>
sortByType === "asc"
? a[sortByName].localeCompare(b[sortByName])
: b[sortByName].localeCompare(a[sortByName])
),
sortByName,
sortByType
}));
};
render = () => (
<div className={app}>
<Table
{...this.state}
clearFilters={this.clearFilters}
handleSort={this.handleSort}
/>
</div>
);
}
export default App;
components / Table / index.js
import React from "react";
import { configure, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import App, { initialState } from "../index";
configure({ adapter: new Adapter() });
describe("App", () => {
let wrapper;
beforeEach(() => {
// before each test, reset the wrapper to initial state
wrapper = mount(<App />);
});
it("should render without errors", () => {
expect(wrapper.find("Table").exists()).toBeTruthy();
expect(wrapper.find("FaSort").length).toEqual(3);
});
it("sorts data by a table header sort button", () => {
// a function to simulate a sort click on the icon next to the "id" table header
const sortById = () => wrapper.find("button#sort-id").simulate("click");
// simulating an initial sort click
sortById();
// next to id should should now be a "sort down" arrow icon
expect(wrapper.find("FaSortDown").length).toEqual(1);
// the "sortByName" state should now be "id"
expect(wrapper.state("sortByName")).toEqual("id");
// the "sortByType" state should now be "desc"
expect(wrapper.state("sortByType")).toEqual("desc");
// the "data" state should be now sorted by desc id
expect(wrapper.state("data")).toEqual([
{
id: "3",
manufacturer: "Honda",
model: "Civic"
},
{
id: "2",
manufacturer: "Toyota",
model: "Tundra"
},
{
id: "1",
manufacturer: "Ford",
model: "Mustang"
}
]);
// simulating the sort click again
sortById();
// next to the "id" should be now be a "sort up" arrow icon
expect(wrapper.find("FaSortUp").length).toEqual(1);
// the "sortByType" state should now be "asc"
expect(wrapper.state("sortByType")).toEqual("asc");
// the "data" state should be sorted by the asc id
expect(wrapper.state("data")).toEqual([
{
id: "1",
manufacturer: "Ford",
model: "Mustang"
},
{
id: "2",
manufacturer: "Toyota",
model: "Tundra"
},
{
id: "3",
manufacturer: "Honda",
model: "Civic"
}
]);
// clicking 'clear filters' button to reset to initial state
wrapper.find("button#clear-filters").simulate("click");
expect(wrapper.state()).toEqual(initialState);
});
});
我正在使用一些ES6速记语法,因此,如果看起来有些混乱,那么这里有一些注意事项:
import React from "react";
import PropTypes from "prop-types";
import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa"; //
import {
table,
thead,
th,
td,
headerColum,
sort,
clear
} from "./Table.module.scss";
const Table = ({
clearFilters,
data,
handleSort,
headers,
sortByName,
sortByType
}) => (
<>
<table className={table}>
<thead className={thead}>
<tr>
{headers.map(name => (
<th className={th} key={name}>
<span className={headerColum}>{name}</span>
<button
id={`sort-${name}`}
className={sort}
type="button"
onClick={() =>
handleSort(
name,
!sortByType || sortByName !== name || sortByType === "asc"
? "desc"
: "asc"
)
}
key={name}
>
{sortByName !== name ? (
<FaSort />
) : sortByType === "asc" ? (
<FaSortUp style={{ color: "#0093fc" }} />
) : (
<FaSortDown style={{ color: "#0093fc" }} />
)}
</button>
</th>
))}
</tr>
</thead>
<tbody>
{data.map(({ id, manufacturer, model }) => (
<tr key={id}>
<td className={td}>{id}</td>
<td className={td}>{manufacturer}</td>
<td className={td}>{model}</td>
</tr>
))}
</tbody>
</table>
<button
type="button"
id="clear-filters"
className={clear}
onClick={clearFilters}
>
Clear Filters
</button>
</>
);
Table.propTypes = {
clearFilters: PropTypes.func.isRequired,
data: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
manufacturer: PropTypes.string.isRequired,
model: PropTypes.string.isRequired
}).isRequired
).isRequired,
handleSort: PropTypes.func.isRequired,
headers: PropTypes.arrayOf(PropTypes.string).isRequired,
sortByName: PropTypes.string,
sortByType: PropTypes.string
};
export default Table;
)是cond ? true : false
的简写。if/else
是具有属性的对象,我们可以执行以下操作:state
,这是以下简称的缩写:<Table {...this.state} />
<Table data={this.state.data} sortByName={this.state.sortByName} />
是异步的,因此我使用callback function来确保状态为最新的setState
。当处理大数据和/或多个this.setState(prevState => ({ ... })
调用时,这可以确保在评估状态时状态是准确的(同步的)。另外,我使用括号this.setState()
作为对象()
隐式地return
函数的结果,例如:{}
将返回const example = () => ({ name: "hello" });
具有object
属性:name
。 const test = example(): // returns { name: "hello" }
更改了数组,因此Array.from()创建了当前数组的新实例,并对该实例(而不是源)进行了更改。根据设计,React不处理可变状态(如果状态发生突变,它不会更新/渲染组件)。通过创建实例,我们用这个新的数组实例覆盖了旧的数组。.sort()
进行排序;例如,sortByType
将按a[id].localCompare(b[id])
的ID字符串按升序对a
对象进行排序。通过反转此顺序,您可以获得降序:b
。b[id].localCompare(a[id])
的简写,将是:const target = event.target;
。我主要在const { target } = event;
组件中使用它,在这里我从传递的属性中拉出Table
:{ property }
<Table {...this.state} />
函数代替mount
,因为mount显示了整个DOM树结构;而浅层仅显示根级DOM结构。例如,shallow
如下所示:
mount
<App>
<div>example</div>
<Child>
<p>I'm a child!</p>
<SecondChild>
<p>I'm a secondary child</p>
<SecondChild>
<Child>
</App>
如下所示:
shallow
由于我要同时针对根级和子级组件进行声明,因此我在<App>
<div>example</div>
<Child />
</App>
上使用mount
-您可以通过使用酶shallow
函数来查看此结构:{{ 1}}。
一些其他资源: