jestapi锛屾湰绡囨枃绔犳垜浼氬厛浠嬬粛涓€涓?codeclass="mq-6">Enzyme锛岀劧鍚庣粨鍚堥」鐩腑鐨勪竴涓湡瀹炵粍浠讹紝鏉ヤ负瀹冪紪鍐欐祴璇曠敤渚嬨€?/p>馃懆鈥嶐煔€Enzyme涓婁竴绡囦腑鎴戜滑鍏跺疄宸茬粡绠€鍗曚粙缁嶄簡enzyme锛"/>

Posted 鍓嶇妫灄

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了相关的知识,希望对你有一定的参考价值。

馃懆鈥嶐煂?鍐欏湪鍓嶉潰

涓婄瘒鏂囩珷鎴戜滑宸茬粡浜嗚В浜嗗墠绔崟鍏冩祴璇曠殑鑳屾櫙鍜屽熀纭€鐨?code class="mq-5">jestapi锛屾湰绡囨枃绔犳垜浼氬厛浠嬬粛涓€涓?code class="mq-6">Enzyme锛岀劧鍚庣粨鍚堥」鐩腑鐨勪竴涓湡瀹炵粍浠讹紝鏉ヤ负瀹冪紪鍐欐祴璇曠敤渚嬨€?/p>

馃懆鈥嶐煔€ Enzyme

涓婁竴绡囦腑鎴戜滑鍏跺疄宸茬粡绠€鍗曚粙缁嶄簡enzyme锛屼絾杩欒繙杩滀笉澶燂紝鍦ㄦ湰绡囩殑缁勪欢娴嬭瘯鐢ㄤ緥缂栧啓涓紝鎴戜滑鏈夊緢澶氬湴鏂硅鐢ㄥ埌瀹冿紝鍥犳杩欓噷涓撻棬鏉ヨ鏄庝竴涓嬨€?/p>

Enzyme鏄敱Airbnb寮€婧愮殑涓€涓?code class="mq-15">React鐨?code class="mq-16">javascript娴嬭瘯宸ュ叿锛屼娇React缁勪欢鐨勮緭鍑烘洿鍔犲鏄撱€?code class="mq-18">Enzyme鐨?code class="mq-19">API鍜?code class="mq-20">jQuery鎿嶄綔DOM涓€鏍风伒娲绘槗鐢紝鍥犱负瀹冧娇鐢ㄧ殑鏄?code class="mq-22">cheerio搴撴潵瑙f瀽铏氭嫙DOM锛岃€?code class="mq-24">cheerio鐨勭洰鏍囧垯鏄仛鏈嶅姟鍣ㄧ鐨?code class="mq-25">jQuery銆?code class="mq-26">Enzyme鍏煎澶у鏁版柇瑷€搴撳拰娴嬭瘯妗嗘灦锛屽chai銆?code class="mq-28">mocha銆?code class="mq-29">jasmine绛夈€?/p>

鈥?/span>

馃檵 鍏充簬瀹夎鍜岄厤缃紝涓婁竴灏忚妭宸茬粡鏈夎繃璇存槑锛岃繖閲屽氨涓嶈禈杩颁簡

甯哥敤鍑芥暟

enzyme涓湁鍑犱釜姣旇緝鏍稿績鐨勫嚱鏁帮紝濡備笅锛?/p>

  • simulate(event, mock)锛氱敤鏉ユā鎷熶簨浠惰Е鍙戯紝 event涓轰簨浠跺悕绉帮紝 mock涓轰竴涓? event object锛?
  • instance()锛氳繑鍥炴祴璇曠粍浠剁殑瀹炰緥锛?
  • find(selector)锛氭牴鎹€夋嫨鍣ㄦ煡鎵捐妭鐐癸紝 selector鍙互鏄? CSS涓殑閫夋嫨鍣紝涔熷彲浠ユ槸缁勪欢鐨勬瀯閫犲嚱鏁帮紝浠ュ強缁勪欢鐨? display name绛夛紱
  • at(index)锛氳繑鍥炰竴涓覆鏌撹繃鐨勫璞★紱
  • text()锛氳繑鍥炲綋鍓嶇粍浠剁殑鏂囨湰鍐呭锛?
  • html()锛氳繑鍥炲綋鍓嶇粍浠剁殑 HTML浠g爜褰㈠紡锛?
  • props()锛氳繑鍥炴牴缁勪欢鐨勬墍鏈夊睘鎬э紱
  • prop(key)锛氳繑鍥炴牴缁勪欢鐨勬寚瀹氬睘鎬э紱
  • state()锛氳繑鍥炴牴缁勪欢鐨勭姸鎬侊紱
  • setState(nextState)锛氳缃牴缁勪欢鐨勭姸鎬侊紱
  • setProps(nextProps)锛氳缃牴缁勪欢鐨勫睘鎬э紱

娓叉煋鏂瑰紡

enzyme 鏀寔涓夌鏂瑰紡鐨勬覆鏌擄細

  • shallow锛氭祬娓叉煋锛屾槸瀵瑰畼鏂圭殑 Shallow Renderer鐨勫皝瑁呫€傚皢缁勪欢娓叉煋鎴? 铏氭嫙DOM瀵硅薄锛屽彧浼氭覆鏌撶涓€灞傦紝瀛愮粍浠跺皢涓嶄細琚覆鏌撳嚭鏉ワ紝鍥犺€屾晥鐜囬潪甯搁珮銆備笉闇€瑕?DOM 鐜锛?骞跺彲浠ヤ娇鐢? jQuery鐨勬柟寮忚闂粍浠剁殑淇℃伅锛?
  • render锛氶潤鎬佹覆鏌?/code>锛屽畠灏? React缁勪欢娓叉煋鎴愰潤鎬佺殑 HTML瀛楃涓诧紝鐒跺悗浣跨敤 Cheerio杩欎釜搴撹В鏋愯繖娈靛瓧绗︿覆锛屽苟杩斿洖涓€涓? Cheerio鐨勫疄渚嬪璞★紝鍙互鐢ㄦ潵鍒嗘瀽缁勪欢鐨? html缁撴瀯锛?
  • mount锛氬畬鍏ㄦ覆鏌?/code>锛屽畠灏嗙粍浠舵覆鏌撳姞杞芥垚涓€涓? 鐪熷疄鐨凞OM鑺傜偣锛岀敤鏉ユ祴璇? DOM API鐨勪氦浜掑拰缁勪欢鐨勭敓鍛藉懆鏈燂紝鐢ㄥ埌浜? jsdom鏉ユā鎷熸祻瑙堝櫒鐜銆?

涓夌鏂规硶涓紝shallow鍜?code class="mq-93">mount鍥犱负杩斿洖鐨勬槸DOM瀵硅薄锛屽彲浠ョ敤simulate杩涜浜や簰妯℃嫙锛岃€?code class="mq-96">render鏂规硶涓嶅彲浠ャ€備竴鑸?code class="mq-97">shallow鏂规硶灏卞彲浠ユ弧瓒抽渶姹傦紝濡傛灉闇€瑕佸瀛愮粍浠惰繘琛屽垽鏂紝闇€瑕佷娇鐢?code class="mq-98">render锛屽鏋滈渶瑕佹祴璇曠粍浠剁殑鐢熷懡鍛ㄦ湡锛岄渶瑕佷娇鐢?code class="mq-99">mount鏂规硶銆?/p>

鈥?/span>

娓叉煋鏂瑰紡閮ㄥ垎鍙傝€冪殑杩欑瘒鏂囩珷

馃惗 鈥滆俯鍧戜箣璺€濆紑鍚?/span>

缁勪欢浠g爜

棣栧厛锛屾潵鐪嬩笅鎴戜滑闇€瑕佸鍏惰繘琛屾祴璇曠殑缁勪欢閮ㄥ垎鐨勪唬鐮侊細

鈥?/span>

鈿狅笍 鍥犱负鐗垫壇鍒板唴閮ㄤ唬鐮侊紝鎵€浠ュ緢澶氬湴鏂归兘鎵撶爜浜嗐€傞噸鍦ㄦ紨绀洪拡瀵逛笉鍚岀被鍨嬬殑娴嬭瘯鐢ㄤ緥鐨勭紪鍐?/p>

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 } = Select
const { RangePicker } = DatePicker
const { confirm } = Modal


export 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,
      onChangethis.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="璇疯緭鍏ヤ骇鍝両D"
                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鍖呰9鐨勯珮闃剁粍浠讹紝椤甸潰灞曠ず濡備笅锛?/p>

鎴戜滑瑕佹坊鍔犵殑娴嬭瘯鐢ㄤ緥濡備笅锛?/p>

1銆侀〉闈㈣兘澶熸甯告覆鏌?/p>

2銆?code class="mq-383">DOM娴嬭瘯锛氭爣棰樺簲璇ヤ负XX褰曞叆绯荤粺

3銆佺粍浠剁敓鍛藉懆鏈熷彲浠ヨ姝e父璋冪敤

4銆佺粍浠跺唴鏂规硶handleSearch锛堝嵆鈥滄煡璇⑩€濇寜閽笂缁戝畾鐨勪簨浠讹級鍙互琚甯歌皟鐢?/p>

5銆佷骇鍝?ID 杈撳叆妗嗗唴瀹规洿鏀瑰悗锛?code class="mq-389">state涓?code class="mq-390">productID鍊间細闅忎箣鍙樺寲

6銆?code class="mq-392">MarketRuleManage缁勪欢搴旇鎺ュ彈鎸囧畾鐨?code class="mq-393">props鍙傛暟

娴嬭瘯椤甸潰蹇収

鏄庣‘浜嗛渶姹傦紝璁╂垜浠紑濮嬬紪鍐欑涓€鐗堢殑娴嬭瘯鐢ㄤ緥浠g爜锛?/p>

import React from "react"
import { mount, shallow } from "enzyme"


import MarketRuleManage from "../../../src/routes/marketRule-manage"

describe("XX褰曞叆绯荤粺椤甸潰", () => {

  // 浣跨敤 snapshot 杩涜 UI 娴嬭瘯
  it("椤甸潰搴旇兘姝e父娓叉煋", () => {
    const wrapper = shallow(<MarketRuleManage />)
    expect(wrapper).toMatchSnapshot()
  })

})

鎵цnpm run test:

鈥?/span>

npm run test瀵瑰簲鐨勮剼鏈槸jest --verbose

閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>鎶ラ敊浜嗭細<code class=Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(MarketRuleManage)".鎰忔€濆氨鏄垜浠渶瑕佺粰connect鍖呰9鐨勭粍浠朵紶閫掍竴涓?code class="mq-427">store銆?/p>

缁忚繃涓€鐣悳绱紝鎴戝湪stackoverflow[1]鎵惧埌浜嗙瓟妗堬紝闇€瑕佷娇鐢?code class="mq-431">redux-mock-store涓殑configureMockStore鏉ユā鎷熶竴涓亣鐨?code class="mq-433">store銆傛潵璋冩暣涓€涓嬫祴璇曚唬鐮侊細

import React from "react"
鉃?span class="mq-440">import { Provider } from "react-redux"
鉃?span class="mq-443">import configureMockStore from "redux-mock-store"
import { mount, shallow } from "enzyme"


import MarketRuleManage from "../../../src/routes/marketRule-manage"

鉃?span class="mq-452">const mockStore = configureMockStore()
鉃?span class="mq-453">const store = mockStore({
鉃?nbsp;marketRuleManage: {
鉃?nbsp;  pagination: {},
鉃?nbsp;   productList: [],
鉃?nbsp;   productDetail: {},
鉃?nbsp; },
鉃晑)

鉃?span class="mq-454">const props = {
鉃?nbsp; match: {
鉃?nbsp;   url: "/",
鉃?nbsp; },
鉃晑

describe("XX褰曞叆绯荤粺椤甸潰", () => {

  // 浣跨敤 snapshot 杩涜 UI 娴嬭瘯
  it("椤甸潰搴旇兘姝e父娓叉煋", () => {
鉃?nbsp;   const wrapper = shallow(<Provider store={store}>
鉃?nbsp;     <MarketRuleManage {...props} />
鉃?nbsp;  </Provider>
)
    expect(wrapper).toMatchSnapshot()
  })

})

鍐嶆杩愯npm run test锛?img data-ratio="0.264750378214826" src="/img?url=https://mmbiz.qpic.cn/mmbiz_png/DByMFBAKmy5eRibevqRborvPJodTxwfpVMwYg06MPTKBZXkS6CKTwllzyyrHJ7oTNEEVzB947tc7JOw6dfwhAbA/640?wx_fmt=png" data-type="png" data-w="1322" _width="368px" class="mq-472" alt="閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>ok锛岀涓€鏉℃祴璇曠敤渚嬮€氳繃浜嗭紝骞朵笖鐢熸垚浜嗗揩鐓х洰褰?code class="mq-473">__snapshots__銆?/p>

娴嬭瘯椤甸潰DOM

鎴戜滑鎺ョ潃寰€涓嬶紝鏉ョ湅绗簩鏉℃祴璇曠敤渚嬶細DOM娴嬭瘯锛氭爣棰樺簲璇ヤ负XX褰曞叆绯荤粺銆?/p>

淇敼娴嬭瘯浠g爜锛?/p>

import React from "react"
import { Provider } from "react-redux"
import configureMockStore from "redux-mock-store"
import { mount, shallow } from "enzyme"


import MarketRuleManage from "../../../src/routes/marketRule-manage"

const mockStore = configureMockStore()
const store = mockStore({
  marketRuleManage: {
    pagination: {},
    productList: [],
    productDetail: {},
  },
})

const props = {
  match: {
    url"/",
  },
}

describe("XX褰曞叆绯荤粺椤甸潰", () => {

  // 浣跨敤 snapshot 杩涜 UI 娴嬭瘯
  it("椤甸潰搴旇兘姝e父娓叉煋", () => {
    const wrapper = shallow(<Provider store={store}>
      <MarketRuleManage {...props} />
    </Provider>
)
    expect(wrapper).toMatchSnapshot()
  })

  // 瀵圭粍浠惰妭鐐硅繘琛屾祴璇?/span>
  it("鏍囬搴斾负'XX褰曞叆绯荤粺'", () => {
    const wrapper = shallow(<Provider store={store}>
      <MarketRuleManage {...props} />
    </Provider>
)
    expect(wrapper.find("h2").text()).toBe("XX褰曞叆绯荤粺")
  })

})

杩愯npm run test锛?img data-ratio="0.36789297658862874" src="/img?url=https://mmbiz.qpic.cn/mmbiz_png/DByMFBAKmy5eRibevqRborvPJodTxwfpViaTjkn2fze0ImqbpBD98Igm63TK2M4dUiaKVunhzm71xlZYkicCzjb5sQ/640?wx_fmt=png" data-type="png" data-w="1196" _width="368px" class="mq-540" alt="閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>绾冲凹锛?code class="mq-541">Method 鈥渢ext鈥?is meant to be run on 1 node. 0 found instead.鎵句笉鍒?code class="mq-542">h2鏍囩锛?/p>

鎴戜滑鍦ㄥ紑绡囦粙缁?code class="mq-544">enzyme鏃讹紝鐭ラ亾瀹冩湁涓夌娓叉煋鏂瑰紡锛岄偅杩欓噷鎴戜滑鏀逛负mount璇曡瘯銆傚啀娆¤繍琛?code class="mq-546">npm run test锛?img data-ratio="0.3739565943238731" src="/img?url=https://mmbiz.qpic.cn/mmbiz_png/DByMFBAKmy5eRibevqRborvPJodTxwfpVsjKJuwOfichdj77lxfOSPicCibhUPqOcpJBbWriaqRKXMmuCK3P3Ap2XCA/640?wx_fmt=png" data-type="png" data-w="1198" _width="368px" class="mq-547" alt="閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>婕備寒锛屽張鍑烘潵涓€涓柊鐨勯敊璇細Invariant Violation: You should not use <Link> outside a <Router>

涓€椤挎悳绱紝鍐嶆鍦?span class="mq-550">stackoverflow[2]鎵惧埌浜嗙瓟妗堬紙涓嶅緱涓嶈 stackoverflow 鐪熼锛夛紝鍥犱负鎴戠殑椤圭洰涓敤鍒颁簡璺敱锛岃€岃繖閲屾槸闇€瑕佸寘瑁呬竴涓嬬殑锛?/p>

import { BrowserRouter } from 'react-router-dom';
import Enzyme, { shallow, mount } from 'enzyme';

import { shape } from 'prop-types';

// Instantiate router context
const router = {
  historynew BrowserRouter().history,
  route: {
    location: {},
    match: {},
  },
};

const createContext = () => ({
  context: { router },
  childContextTypes: { router: shape({}) },
});

export function mountWrap(node{
  return mount(node, createContext());
}

export function shallowWrap(node{
  return shallow(node, createContext());
}

杩欓噷鎴戞妸杩欓儴鍒嗕唬鐮佹彁鍙栧埌浜嗕竴涓崟鐙殑routerWrapper.js鏂囦欢涓€?/p>

鐒跺悗鎴戜滑淇敼涓嬫祴璇曚唬鐮侊細

import React from "react"
import { Provider } from "react-redux"
import configureMockStore from "redux-mock-store"
import { mount, shallow } from "enzyme"

import MarketRuleManage from "../../../src/routes/marketRule-manage"
鉃?span class="mq-610">import {
鉃?nbsp; mountWrap,
鉃?nbsp; shallowWithIntlWrap,
鉃?nbsp; shallowWrap,
鉃晑 from "../../utils/routerWrapper"

const mockStore = configureMockStore()
const store = mockStore({
  marketRuleManage: {
    pagination: {},
    productList: [],
    productDetail: {},
  },
})

const props = {
  match: {
    url"/",
  },
}

鉃?span class="mq-623">const wrappedShallow = () =>
  shallowWrap(
    <Provider store={store}>
      <MarketRuleManage {...props} />
    </Provider>

  )

鉃?span class="mq-636">const wrappedMount = () =>
  mountWrap(
    <Provider store={store}>
      <MarketRuleManage {...props} />
    </Provider>

  )

describe("XX褰曞叆绯荤粺椤甸潰", () => {

  // 浣跨敤 snapshot 杩涜 UI 娴嬭瘯
  it("椤甸潰搴旇兘姝e父娓叉煋", () => {
馃敡  const wrapper = wrappedShallow()
    expect(wrapper).toMatchSnapshot()
  })

  // 瀵圭粍浠惰妭鐐硅繘琛屾祴璇?/span>
  it("鏍囬搴斾负'XX褰曞叆绯荤粺'", () => {
 馃敡   const wrapper = wrappedMount()
    expect(wrapper.find("h2").text()).toBe("XX褰曞叆绯荤粺")
  })

})
鈥?/span>

鈿狅笍 娉ㄦ剰浠g爜涓殑鍥炬爣锛屸灂 浠h〃鏂板浠g爜锛岎煍?浠h〃浠g爜鏈変慨鏀?/p>

杩愯npm run test锛?img data-ratio="0.25925925925925924" src="/img?url=https://mmbiz.qpic.cn/mmbiz_png/DByMFBAKmy5eRibevqRborvPJodTxwfpVUpJwmXPeIAh33MVLxf9Lht4cKAHz7IRNvPYMOClVf4q2BQ7P54b3cA/640?wx_fmt=png" data-type="png" data-w="1188" _width="368px" class="mq-663" alt="閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>

鎶ラ敊TypeError: window.matchMedia is not a function锛岃繖鍙堟槸鍟ラ敊璇晩锛侊紒

鏌ラ槄鐩稿叧璧勬枡锛?code class="mq-667">matchMedia鏄寕杞藉湪window涓婄殑涓€涓璞★紝琛ㄧず鎸囧畾鐨勫獟浣撴煡璇㈠瓧绗︿覆瑙f瀽鍚庣殑缁撴灉銆傚畠鍙互鐩戝惉浜嬩欢銆傞€氳繃鐩戝惉锛屽湪鏌ヨ缁撴灉鍙戠敓鍙樺寲鏃讹紝灏辫皟鐢ㄦ寚瀹氱殑鍥炶皟鍑芥暟銆?/p>

鏄剧劧jest鍗曞厓娴嬭瘯闇€瑕佸matchMedia瀵硅薄鍋氫竴涓?code class="mq-672">mock銆傜粡杩囨悳绱紝鍦?span class="mq-673">stackoverflow[3]杩欓噷鎵惧埌浜嗙瓟妗堬細

Object.defineProperty(window'matchMedia', {
  writabletrue,
  value: jest.fn().mockImplementation(query => ({
    matchesfalse,
    media: query,
    onchangenull,
    addListener: jest.fn(), // Deprecated
    removeListener: jest.fn(), // Deprecated
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

鎶婁笂杩颁唬鐮佸啓鍒颁竴涓崟鐙殑matchMedia.js鏂囦欢涓紝鐒跺悗鍦ㄤ笂闈㈢殑routerWrapper.js鏂囦欢涓紩鍏ワ細

import { mount, shallow } from "enzyme"
import { mountWithIntl, shallowWithIntl } from "enzyme-react-intl"
import { shape } from "prop-types"
import { BrowserRouter } from "react-router-dom"
鉃?span class="mq-716">import "./matchMedia"

// Instantiate router context
const router = {
  historynew BrowserRouter().history,
  route: {
    location: {},
    match: {},
  },
}

const createContext = () => ({
  context: { router },
  childContextTypes: { router: shape({}) },
})
// ...

姝ゆ椂閲嶆柊杩愯npm run test锛?/p>

閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>ok锛岀浜屾潯娴嬭瘯鐢ㄤ緥涔熼『鍒╅€氳繃浜嗭綖</p> 
 <h3 data-tool=娴嬭瘯鐢熷懡鍛ㄦ湡

鏉ョ湅绗笁鏉℃祴璇?case锛氱粍浠剁敓鍛藉懆鏈熷彲浠ヨ姝e父璋冪敤

浣跨敤spyOn鏉?code class="mq-742">mock缁勪欢鐨?code class="mq-743">componentDidMount銆傛坊鍔犳祴璇曚唬鐮侊細

// 娴嬭瘯缁勪欢鐢熷懡鍛ㄦ湡
it("缁勪欢鐢熷懡鍛ㄦ湡", () => {
  const componentDidMountSpy = jest.spyOn(
    MarketRuleManage.prototype,
    "componentDidMount"
  )
  const wrapper = wrappedMount()

  expect(componentDidMountSpy).toHaveBeenCalled()

  componentDidMountSpy.mockRestore()
})

杩愯npm run test:閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?></p> 
 <p data-tool=鐢ㄤ緥椤哄埄閫氳繃锝?/p>

鈥?/span>

璁板緱瑕佸湪鐢ㄤ緥鏈€鍚庡mock鐨勫嚱鏁拌繘琛?code class="mq-760">mockRestore()

娴嬭瘯缁勪欢鐨勫唴閮ㄥ嚱鏁?span class="mq-763">

鎺ョ潃鏉ョ湅绗洓鏉℃祴璇?case锛氱粍浠跺唴鏂规硶handleSearch锛堝嵆鈥滄煡璇⑩€濇寜閽笂缁戝畾鐨勪簨浠讹級鍙互琚甯歌皟鐢ㄣ€?/p>

娣诲姞娴嬭瘯浠g爜锛?/p>

// 娴嬭瘯缁勪欢鐨勫唴閮ㄥ嚱鏁?/span>
it("缁勪欢鍐呮柟娉昲andleSearch鍙互琚甯歌皟鐢?, () => {
  const wrapper = wrappedMount()

  const instance = wrapper.instance()
  const spyFunction = jest.spyOn(instance, "handleSearch")
  instance.handleSearch()
  expect(spyFunction).toHaveBeenCalled() // handleSearch琚皟鐢ㄤ簡涓€娆?/span>
  spyFunction.mockRestore()
})

鎵цnpm run test:閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>鎶ラ敊浜嗭細<code class=Cannot spy the handleSearch property because it is not a function; undefined given instead锛?/p>

娌″姙娉曪紝鍙兘鎼滀竴涓嬶紝瀵绘眰绛旀锛岄鍏堝湪stackoverflow[4]寰楀埌浜嗗涓嬫柟妗堬細閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>澶ц嚧鎰忔€濆氨鏄鐢?code class=shallowWithIntl()鏉ュ寘瑁逛竴涓嬬粍浠讹紝鐒跺悗琚寘瑁圭殑缁勪欢闇€瑕佺敤dive()涓€涓嬨€?/p>

鎴戠珛鍗充慨鏀逛簡浠g爜锛屽啀娆¤繍琛?code class="mq-788">npm run test锛岀粨鏋滀緷鐒舵槸涓€鏍风殑銆?/p>

娌″姙娉曪紝鎺ョ潃鎼滅储锛屽湪enzyme 鐨?365issue[5]鐪嬪埌浜嗕技涔庡緢鎺ヨ繎鐨勭瓟妗堬細閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>灏辨槸鍦?code class=jest.spyOn()涔嬪悗瀵圭粍浠惰繘琛屽己鍒舵洿鏂帮細wrapper.instance().forceUpdate()鍜?code class="mq-795">wrapper.update()銆?/p>

鎺ョ潃淇敼浠g爜銆佽皟璇曪紝渚濈劧鏃犳晥銆?/p>

鎴戯紝閮侀椃浜嗐€傘€傘€?/p>

涓棿涔熸壘浜嗗緢澶氭柟妗堬紝浣嗛兘娌$敤銆?/p>

杩欐椂姝eソ鍦ㄥ唴閮ㄦ枃妗d笂鐪嬪埌浜嗕竴涓叾浠?BU 澶т浆鍐欑殑鍗曞厓娴嬭瘯鎬荤粨锛屼簬鏄氨鍘氱潃鑴哥毊鍘绘壘澶т浆鑱婁簡鑱婏紝鏋滀笉鍏剁劧锛岃繖鎷涘緢鍑戞晥锛屼竴璇偣閱掓ⅵ涓汉锛氫綘鐨勭粍浠惰connect鍖呰9锛屾槸涓€涓珮闃剁粍浠讹紝闇€瑕佹嬁instance涔嬪墠鍋氫笅find鎿嶄綔锛岃繖鏍锋墠鑳芥嬁鍒扮湡瀹炵粍浠剁殑瀹炰緥銆?/p>

鎰熻阿瀹屽ぇ浣紝鎴戠珛鍗冲幓瀹炶返锛?/p>

// 娴嬭瘯缁勪欢鐨勫唴閮ㄥ嚱鏁?/span>
it("缁勪欢鍐呮柟娉昲andleSearch鍙互琚甯歌皟鐢?, () => {
  const wrapper = wrappedMount()

  const instance = wrapper.find("MarketRuleManage").instance()
  const spyFunction = jest.spyOn(instance, "handleSearch")
  instance.handleSearch()
  expect(spyFunction).toHaveBeenCalled() // handleSearch琚皟鐢ㄤ簡涓€娆?/span>
  spyFunction.mockRestore()
})

杩笉鍙婂緟鐨?code class="mq-816">npm run test:閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>鍡紝娴嬭瘯鐢ㄤ緥椤哄埄閫氳繃锛岀湡棣欙紒</p> 
 <p data-tool=鍐欏畬杩欎釜鐢ㄤ緥锛屾垜涓嶇鍙嶆€濓細灏忎紮瀛愶紝鍩虹杩樻槸涓嶅お琛屽晩

杩樻槸瑕佸鍐欏瀹炶返鎵嶈鍟婏紒

娴嬭瘯缁勪欢 state

搴熻瘽灏戣锛屾垜浠潵鐪嬬浜旀潯娴嬭瘯鐢ㄤ緥锛氫骇鍝?ID 杈撳叆妗嗗唴瀹规洿鏀瑰悗锛?code class="mq-824">state涓?code class="mq-825">productID鍊间細闅忎箣鍙樺寲

娣诲姞娴嬭瘯浠g爜锛?/p>

// 娴嬭瘯缁勪欢state
it("浜у搧ID杈撳叆妗嗗唴瀹规洿鏀瑰悗锛宻tate涓璸roductID浼氶殢涔嬪彉鍖?, () => {
  const wrapper = wrappedMount()
  const inputElm = wrapper.find("[data-test='marketingRuleID']").first()
  const userInput = 1111
  inputElm.simulate("change", {
    target: { value: userInput },
  })
  // console.log(
  //   "wrapper",
  //   wrapper.find("MarketRuleManage").instance().state.productID
  // )
  const updateProductID = wrapper.find("MarketRuleManage").instance().state
    .productID

  expect(updateProductID).toEqual(userInput)
})

杩欓噷鍏跺疄鏄ā鎷熺敤鎴风殑杈撳叆琛屼负锛岀劧鍚庝娇鐢?code class="mq-847">simulate鐩戝惉杈撳叆妗嗙殑change浜嬩欢锛屾渶缁堝垽鏂?code class="mq-849">input鐨勬敼鍙樻槸鍚﹁兘鍚屾鍒?code class="mq-850">state涓€?/p>

鈥?/span>

杩欎釜鐢ㄤ緥鍏跺疄鏄湁鐐?code class="mq-854">BDD鐨勬剰鎬濅簡

鎴戜滑杩愯npm run test锛?img data-ratio="0.389413988657845" src="/img?url=https://mmbiz.qpic.cn/mmbiz_png/DByMFBAKmy5eRibevqRborvPJodTxwfpVYhmNblld6GqScKlL9Tu0RgzL0RH0BKp0NHAv9icLc3gzq6cMOUjV4SQ/640?wx_fmt=png" data-type="png" data-w="1058" _width="368px" class="mq-857" alt="閭d簺骞撮敊杩囩殑React缁勪欢鍗曞厓娴嬭瘯锛堜笅锛?>鐢ㄤ緥椤哄埄閫氳繃锝?/p>

娴嬭瘯缁勪欢 props

缁堜簬鏉ュ埌浜嗘渶鍚庝竴涓祴璇曠敤渚嬶細MarketRuleManage缁勪欢搴旇鎺ュ彈鎸囧畾鐨?code class="mq-863">props鍙傛暟

娣诲姞娴嬭瘯浠g爜锛?/p>

// 娴嬭瘯缁勪欢props
it("MarketRuleManage缁勪欢搴旇鎺ユ敹鎸囧畾鐨刾rops", () => {
  const wrapper = wrappedMount()
  // console.log("wrapper", wrapper.find("MarketRuleManage").instance())
  const instance = wrapper.find("MarketRuleManage").instance()
  expect(instance.props.match).toBeTruthy()
  expect(instance.props.pagination).toBeTruthy()
  expect(instance.props.productList).toBeTruthy()
  expect(instance.props.productDetail).toBeTruthy()
  expect(instance.props.queryMarketRules).toBeTruthy()
  expect(instance.props.editMarketRule).toBeTruthy()
  expect(instance.props.delMarketRule).toBeTruthy()
  expect(instance.props.deleteByRuleId).toBeTruthy()
  expect(instance.props.columns).toBeTruthy()
})

鎵цnpm run test锛?/p>

鍒拌繖閲岋紝鎴戜滑鎵€鏈夌殑娴嬭瘯鐢ㄤ緥灏辨墽琛屽畬浜嗭綖

鎴戜滑鎵ц鐨勮繖 6 鏉$敤渚嬪熀鏈彲浠ユ瘮杈冨叏闈㈢殑娑电洊React鐨?code class="mq-879">缁勪欢鍗曞厓娴嬭瘯浜嗭紝褰撶劧鍥犱负鎴戜滑杩欓噷鐢ㄧ殑鏄?code class="mq-880">dva锛岄偅涔堥毦鍏嶄篃瑕佸model杩涜娴嬭瘯锛岃繖閲屾垜鏀句竴涓嬩竴涓ぇ浣殑dva-example-user-dashboard 鍗曞厓娴嬭瘯[6]锛岄噷闈㈠凡缁忓垪涓剧殑姣旇緝璇︾粏浜嗭紝鎴戝氨涓嶇彮闂ㄥ紕鏂т簡銆?/p>

馃寛 鐖卞績涓夎繛

1.濡傛灉瑙夊緱杩欑瘒鏂囩珷杩樹笉閿欙紝鏉ヤ釜鍒嗕韩銆佺偣璧炪€佸湪鐪?/strong>涓夎繛鍚э紝璁╂洿澶氱殑浜轰篃鐪嬪埌锝?/p>

3.鐗规畩闃舵锛屽甫濂藉彛缃╋紝鍋氬ソ涓汉闃叉姢銆?/p>