写在后面上篇文章咱们曾经理解了前端单元测试的背景和根底的jestapi,本篇文章我会先介绍一下Enzyme,而后联合我的项目中的一个实在组件,来为它编写测试用例。
Enzyme上一篇中咱们其实曾经简略介绍了enzyme,但这远远不够,在本篇的组件测试用例编写中,咱们有很多中央要用到它,因而这里专门来阐明一下。
Enzyme是由Airbnb开源的一个React的JavaScript测试工具,使React组件的输入更加容易。Enzyme的API和jQuery操作DOM一样灵便易用,因为它应用的是cheerio库来解析虚构DOM,而cheerio的指标则是做服务器端的jQuery。Enzyme兼容大多数断言库和测试框架,如chai、mocha、jasmine等。
对于装置和配置,上一大节曾经有过阐明,这里就不赘述了罕用函数enzyme中有几个比拟外围的函数,如下:
simulate(event, mock):用来模仿事件触发,event为事件名称,mock为一个event object;instance():返回测试组件的实例;find(selector):依据选择器查找节点,selector能够是CSS中的选择器,也能够是组件的构造函数,以及组件的display name等;at(index):返回一个渲染过的对象;text():返回以后组件的文本内容;html(): 返回以后组件的HTML代码模式;props():返回根组件的所有属性;prop(key):返回根组件的指定属性;state():返回根组件的状态;setState(nextState):设置根组件的状态;setProps(nextProps):设置根组件的属性;渲染形式enzyme 反对三种形式的渲染:
shallow:浅渲染,是对官网的Shallow Renderer的封装。将组件渲染成虚构DOM对象,只会渲染第一层,子组件将不会被渲染进去,因此效率十分高。不须要 DOM 环境, 并能够应用jQuery的形式拜访组件的信息;render:动态渲染,它将React组件渲染成动态的HTML字符串,而后应用Cheerio这个库解析这段字符串,并返回一个Cheerio的实例对象,能够用来剖析组件的html构造;mount:齐全渲染,它将组件渲染加载成一个实在的DOM节点,用来测试DOM API的交互和组件的生命周期,用到了jsdom来模仿浏览器环境。三种办法中,shallow和mount因为返回的是DOM对象,能够用simulate进行交互模仿,而render办法不能够。个别shallow办法就能够满足需要,如果须要对子组件进行判断,须要应用render,如果须要测试组件的生命周期,须要应用mount办法。
渲染形式局部参考的这篇文章 “踩坑之路”开启组件代码首先,来看下咱们须要对其进行测试的组件局部的代码:
⚠️ 因为牵扯到外部代码,所以很多中央都打码了。重在演示针对不同类型的测试用例的编写import { SearchOutlined } from "@ant-design/icons"import { Button, Col, DatePicker, Input, message, Modal, Row, Select, Table,} from "antd"import { connect } from "dva"import { Link, routerRedux } from "dva/router"import moment from "moment"import PropTypes from "prop-types"import React from "react"const { Option } = Selectconst { RangePicker } = DatePickerconst { confirm } = Modalexport class MarketRuleManage extends React.Component { constructor(props) { super(props) this.state = { productID: "", } } componentDidMount() { // console.log("componentDidMount生命周期") } getTableColumns = (columns) => { return [ ...columns, { key: "operation", title: "操作", dataIndex: "operation", render: (_text, record, _index) => { return ( <React.Fragment> <Button type="primary" size="small" style={{ marginRight: "5px" }} onClick={() => this.handleRuleEdit(record)} > 编辑 </Button> <Button type="danger" size="small" onClick={() => this.handleRuleDel(record)} > 删除 </Button> </React.Fragment> ) }, }, ] } handleSearch = () => { console.log("点击查问") const { pagination } = this.props pagination.current = 1 this.handleTableChange(pagination) } render() { // console.log("props11111", this.props) const { pagination, productList, columns, match } = this.props const { selectedRowKeys } = this.state const rowSelection = { selectedRowKeys, onChange: this.onSelectChange, } const hasSelected = selectedRowKeys.length > 0 return ( <div className="content-box marketRule-container"> <h2>XX录入零碎</h2> <Row> <Col className="tool-bar"> <div className="filter-span"> <label>产品ID</label> <Input data-test="marketingRuleID" style={{ width: 120, marginRight: "20px", marginLeft: "10px" }} placeholder="请输出产品ID" maxLength={25} onChange={this.handlemarketingRuleIDChange} ></Input> <Button type="primary" icon={<SearchOutlined />} style={{ marginRight: "15px" }} onClick={() => this.handleSearch()} data-test="handleSearch" > 查问 </Button> </div> </Col> </Row> <Row> <Col> <Table tableLayout="fixed" bordered="true" rowKey={(record) => `${record.ruleid}`} style={{ marginTop: "20px" }} pagination={{ ...pagination, }} columns={this.getTableColumns(columns)} dataSource={productList} rowSelection={rowSelection} onChange={this.handleTableChange} ></Table> </Col> </Row> </div> ) }MarketRuleManage.prototypes = { columns: PropTypes.array,}MarketRuleManage.defaultProps = { columns: [ { key: "xxx", title: "产品ID", dataIndex: "xxx", width: "10%", align: "center", }, { key: "xxx", title: "产品名称", dataIndex: "xxx", align: "center", }, { key: "xxx", title: "库存", dataIndex: "xxx", align: "center", // width: "12%" }, { key: "xxx", title: "流动有效期开始", dataIndex: "xxx", // width: "20%", align: "center", render: (text) => { return text ? moment(text).format("YYYY-MM-DD HH:mm:ss") : null }, }, { key: "xxx", title: "流动有效期完结", dataIndex: "xxx", // width: "20%", align: "center", render: (text) => { return text ? moment(text).format("YYYY-MM-DD HH:mm:ss") : null }, }, ],}const mapStateToProps = ({ marketRuleManage }) => ({ pagination: marketRuleManage.pagination, productList: marketRuleManage.productList, productDetail: marketRuleManage.productDetail,})const mapDispatchToProps = (dispatch) => ({ queryMarketRules: (data) => dispatch({ type: "marketRuleManage/queryRules", payload: data }), editMarketRule: (data) => dispatch({ type: "marketRuleManage/editMarketRule", payload: data }), delMarketRule: (data, cb) => dispatch({ type: "marketRuleManage/delMarketRule", payload: data, cb }), deleteByRuleId: (data, cb) => dispatch({ type: "marketRuleManage/deleteByRuleId", payload: data, cb }),})export default connect(mapStateToProps, mapDispatchToProps)(MarketRuleManage)简略介绍一下组件的性能:这是一个被connect包裹的高阶组件,页面展现如下:
...