<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                &emsp;&emsp;測試不僅可以發現和預防問題,還能降低風險、減少企業損失。在React中,涌現了多種測試框架,本節會對其中的Jest和Enzyme做詳細的講解。 ## 一、Jest &emsp;&emsp; [Jest](https://jestjs.io/docs/zh-Hans/getting-started)是由Facebook開源的一個測試框架,可無縫兼容[React項目](https://jestjs.io/docs/zh-Hans/tutorial-react),專注簡單,推崇零配置,開箱即用的宗旨,用于邏輯和組件的單元測試。它的語法和斷言與Jasmine類似,并且還集成了快照測試、Mock、覆蓋率報告等功能,支持多進程并行運行測試,在內部使用JSDOM操作DOM,[JSDOM](https://github.com/jsdom/jsdom)是一種模擬的DOM環境,其行為類似于常規瀏覽器,可用來與用戶交互、在節點上派發事件等。 **1)運行** &emsp;&emsp;為了便于運行Jest,本文使用[Create React App](https://www.html.cn/create-react-app/docs/running-tests/)創建項目,命令如下所示。 ~~~ npx create-react-app my-app ~~~ &emsp;&emsp;只要把測試文件放置在\_\_tests\_\_目錄內,或將它們的名稱添加.test.js或.spec.js后綴,并保存在項目的src目錄中的任何深度,就能被Jest檢測到。當運行下面的命令時,可得到相關的測試結果。 ~~~ npm test ~~~ &emsp;&emsp;默認情況下,Jest每次只運行與本次更改的文件相關的測試用例。 **2)創建測試** &emsp;&emsp;如果要創建測試用例(Test Case),那么需要使用[test()](https://jestjs.io/docs/zh-Hans/api#testname-fn-timeout)或it()函數,其第一個參數是測試名稱,第二個參數是包含測試代碼的回調函數,如下所示。 ~~~ test("two plus two is four", () => { expect(2 + 2).toBe(4); }); ~~~ &emsp;&emsp;[expect()](https://jestjs.io/docs/zh-Hans/expect#expectvalue)函數用于斷言,它能接收一個實際值,并將其作為結果與匹配器中的期望值做比較。如果匹配失敗,那么就會在控制臺輸出相應的錯誤提示。 &emsp;&emsp;[describe()](https://jestjs.io/docs/zh-Hans/api#describename-fn)函數可將測試用例進行邏輯分組,其第一個參數可定義分組的名稱,如下所示。 ~~~ describe("my test case", () => { test("one plus one is two", () => { expect(1 + 1).toBe(2); }); test("two plus two is four", () => { expect(2 + 2).toBe(4); }); }); ~~~ **3)匹配器** &emsp;&emsp;通過[匹配器](https://jestjs.io/docs/zh-Hans/using-matchers)(Matcher)可以各種方式來測試代碼,例如之前示例中的toBe()就是一個匹配器,它使用Object.is()來測試精確匹配,如果要檢查對象是否相等,可改用toEqual(),如下所示。 ~~~ test("object assignment", () => { const data = { name: "strick" }; data["age"] = 28; expect(data).toEqual({ name: "strick", age: 28 }); }); ~~~ &emsp;&emsp;其它常用的匹配器還有區分undefined、null和布爾值、比較數字、匹配字符串、檢查數組或可迭代對象是否包含某個特定項、測試拋出的錯誤等功能。 &emsp;&emsp;所有的匹配器都可以通過.not取反,例如驗證toBeUndefined()不能匹配null,如下所示。 ~~~ test("null is not undefined", () => { expect(null).not.toBeUndefined(); }); ~~~ **4)異步測試** &emsp;&emsp;Jest提供了多種方式來[測試異步代碼](https://jestjs.io/docs/zh-Hans/asynchronous),包括回調函數、Promise和Async/Await,接下來會逐個講解用法。 &emsp;&emsp;(1)默認情況下,Jest測試一旦執行到末尾就會完成,例如有一個check()函數(如下所示),它能接收一個回調函數,一旦check()執行結束,此測試就會在沒有執行回調函數前結束。 ~~~ function check(func) { const success = true; func(success); } test("the data is truth", () => { function callback(data) { expect(data).toBeTruthy(); } check(callback); }); ~~~ &emsp;&emsp;若要解決此問題,可為test()的回調函數傳遞一個名為done的函數參數,Jest會等done()回調函數執行完后,再結束測試,如下所示。 ~~~ test("the data is truth", done => { function callback(data) { expect(data).toBeTruthy(); done(); } check(callback); }); ~~~ &emsp;&emsp;(2)當異步代碼返回Promise對象時,Jest會等待其狀態的變化。如果狀態變為已完成,那么得使用then()方法;如果狀態變為已拒絕,那么得使用catch()方法,如下所示。 ~~~ //狀態為已完成 function checkResolve() { return new Promise((resolve, reject) => { resolve(true); }); } test("the data is truth", () => { return checkResolve().then(data => { expect(data).toBeTruthy(); }); }); //狀態為已拒絕 function checkReject() { return new Promise((resolve, reject) => { reject(false); }); } test("the data is falsity", () => { return checkReject().catch(data => { expect(data).toBeFalsy(); }); }); ~~~ &emsp;&emsp;注意,要將Promise對象作為test()的回調函數的返回值,以免測試提前完成,導致沒有進行方法鏈中的斷言。 &emsp;&emsp;在expect語句中也可以使用.resolves或.rejects兩種匹配器來處理Promise的兩種狀態,如下所示,語法更為簡潔。 ~~~ test("the data is truth", () => { expect(checkResolve()).resolves.toBeTruthy(); }); test("the data is falsity", () => { expect(checkReject()).rejects.toBeFalsy(); }); ~~~ &emsp;&emsp;(3)在測試中使用async和await兩個關鍵字,也可以匹配Promise對象,例如斷言checkResolve()的處理結果,如下所示。 ~~~ test("the data is truth", async () => { const data = await checkResolve(); expect(data).toBeTruthy(); }); ~~~ &emsp;&emsp;它們也能用來測試已拒絕狀態的Promise,如下所示,其中assertions()用于驗證在測試中是否執行了指定數量的斷言。 ~~~ function checkError() { return new Promise((resolve, reject) => { reject(); }).catch(() => { throw "error"; }); } test("the check fails with an error", async () => { expect.assertions(1); try { await checkError(); } catch (e) { expect(e).toMatch("error"); } }); ~~~ &emsp;&emsp;aysnc和awiat還可以與.resolves或.rejects結合使用,如下所示。 ~~~ test("the data is truth", async () => { await expect(checkResolve()).resolves.toBeTruthy(); }); test("the check fails with an error", async () => { await expect(checkError()).rejects.toMatch("error"); }); ~~~ **5)輔助函數** &emsp;&emsp;有時候,在運行測試前需要做些準備工作,而在運行測試之后又需要做些整理工作,Jest提供了四個相關的[輔助函數](https://jestjs.io/docs/zh-Hans/setup-teardown)來處理這兩類工作,如下所列。 &emsp;&emsp;(1)beforeAll()和afterAll()會在所有測試用例之前和之后執行一次。 &emsp;&emsp;(2)beforeEach()和afterEach()會在每個測試用例之前和之后執行,并且可以像異步測試那樣處理異步代碼。 &emsp;&emsp;假設在四個輔助函數中輸出各自的函數名稱,并且有兩個測試用例,如下代碼所示。 ~~~ beforeAll(() => { console.log("beforeAll"); }); afterAll(() => { console.log("afterAll"); }); beforeEach(() => { console.log("beforeEach"); }); afterEach(() => { console.log("afterEach"); }); test("first", () => { expect(2).toBeGreaterThan(1); }); test("second", () => { expect(2).toBeLessThan(3); }); ~~~ &emsp;&emsp;每次運行測試,在控制臺將依次打印出“beforeAll”,兩對“beforeEach”和“afterEach”,“afterAll”。 &emsp;&emsp;當通過describe()對測試用例進行分組時(如下所示),外部的beforeEach()和afterEach()會優先執行。 ~~~ describe("scoped", () => { beforeEach(() => console.log("inner beforeEach")); afterEach(() => console.log("inner afterEach")); test("third", () => { expect([1, 2]).toContain(1); }); }); ~~~ **6)Mock** &emsp;&emsp;Jest內置了[Mock函數](https://jestjs.io/docs/zh-Hans/mock-functions),可用于擦除函數的實際實現來測試代碼之間的連接,捕獲函數的調用和參數、配置其返回值等。 &emsp;&emsp;假設要測試一個自定義的forEach()函數的內部實現,那么可以使用jest.fn()創建一個Mock函數,然后通過檢查它的mock屬性來確保回調函數是否在按預期調用,如下所示。 ~~~ function forEach(items, callback) { for (let index = 0; index < items.length; index++) { callback(items[index]); } } test("forEach", () => { const mockFunc = jest.fn(x => 42 + x); forEach([0, 1], mockFunc); expect(mockFunc.mock.calls.length).toBe(2); //此Mock函數被調用了兩次 expect(mockFunc.mock.calls[0][0]).toBe(0); //第一次調用函數時的第一個參數是0 expect(mockFunc.mock.calls[1][0]).toBe(1); //第二次調用函數時的第一個參數是1 expect(mockFunc.mock.results[0].value).toBe(42); //第一次函數調用的返回值是42 }); ~~~ &emsp;&emsp;每個Mock函數都會包含一個特殊的mock屬性,記錄了函數如何被調用、調用時的返回值等信息,通過該屬性還能追蹤每次調用時的this的值。如果要用Mock函數注入返回值,那么可以像下面這樣鏈式的添加,首次調用返回10,第二次調用返回“x”,接下來的調用都返回true。其中mockName()方法可為Mock函數命名,該名稱將在輸出的日志中顯示,可替換掉默認的“jest.fn()”。 ~~~ const myMock = jest.fn().mockName("returnValue"); myMock .mockReturnValueOnce(10) .mockReturnValueOnce("x") .mockReturnValue(true); console.log(myMock(), myMock(), myMock(), myMock()); //10, 'x', true, true ~~~ &emsp;&emsp;Mock函數還可以模擬模塊,例如攔截axios請求得到的數據,如下代碼所示,為.get提供了一個mockResolvedValue()方法,它會返回用于測試的假數據。 ~~~ import axios from "axios"; jest.mock("axios"); class Users { static all() { return axios.get("./users.json").then(resp => resp.data); } } test("should fetch users", () => { const users = [{ name: "strick" }]; const resp = { data: users }; axios.get.mockResolvedValue(resp); return Users.all().then(data => expect(data).toEqual(users)); }); ~~~ &emsp;&emsp;原生的定時器函數測試起來并不方便,通過jest.useFakeTimers()可以模擬定時器函數,如下所示。 ~~~ function timerGame() { setTimeout(() => { console.log("start"); }, 1000); } jest.useFakeTimers(); test("setTimeout", () => { timerGame(); expect(setTimeout).toHaveBeenCalledTimes(1);       //調用了1次 expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000); //1秒后執行回調 }); ~~~ &emsp;&emsp;Jest模擬出的定時器函數還有快進到正確的時間點、執行當前正在等待的定時器等功能。 **7)快照測試** &emsp;&emsp;Jest提供的[快照測試](https://jestjs.io/docs/zh-Hans/snapshot-testing)(Spapshot Testing)是一種高效的UI測試,它會將React組件序列化成純文本(即快照)并保存在硬盤中,每次測試就把當前生成的快照與保存的快照進行對比,接下來用一個例子來介紹快照測試的用法。 &emsp;&emsp;首先創建一個Link組件,它會渲染出一條包含onMouseEnter事件的鏈接,當鼠標移動到這條鏈接時,會改變它的class屬性。 ~~~ import React from "react"; const STATUS = { HOVERED: "hovered", NORMAL: "normal" }; export default class Link extends React.Component { constructor(props) { super(props); this._onMouseEnter = this._onMouseEnter.bind(this); this.state = { class: STATUS.NORMAL }; } _onMouseEnter() { this.setState({ class: STATUS.HOVERED }); } render() { return ( <a href="#" className={this.state.class} onMouseEnter={this._onMouseEnter} > {this.props.children} </a> ); } } ~~~ &emsp;&emsp;然后創建測試文件spapshot.test.js,在其內部,除了要引入Link組件之外,還得引入[react-test-renderer](https://zh-hans.reactjs.org/docs/test-renderer.html),它不依賴瀏覽器和JSDOM,可將React組件渲染成JavaScript對象(即快照)。 ~~~ import React from "react"; import Link from "./Link"; import renderer from "react-test-renderer"; test("Link changes the class when hovered", () => { const component = renderer.create(<Link>Strick</Link>); let tree = component.toJSON(); expect(tree).toMatchSnapshot(); tree.props.onMouseEnter(); //觸發事件 tree = component.toJSON(); //重新渲染 expect(tree).toMatchSnapshot(); }); ~~~ &emsp;&emsp;在第一次運行測試時,會自動創建\_\_snapshots\_\_目錄,放置對應的快照文件spapshot.test.js.snap,其內容如下所示,包含兩張快照,第二張是觸發onMouseEnter事件后生成的。 ~~~ exports[`Link changes the class when hovered 1`] = ` <a className="normal" href="#" onMouseEnter={[Function]} > Strick </a> `; exports[`Link changes the class when hovered 2`] = ` <a className="hovered" href="#" onMouseEnter={[Function]} > Strick </a> `; ~~~ &emsp;&emsp;如果要刷新保存的快照,除了手動刪除之外,還可以通過jest -u命令實現。 ## 二、Enzyme &emsp;&emsp;[Enzyme](https://airbnb.io/enzyme/)是一款用于React組件的測試框架,可處理渲染出的DOM結構,開放的API類似于jQuery的語法,提供了三種不同的方式來測試組件:淺層渲染(Shallow Rendering)、完全渲染(Full Rendering)和靜態渲染(Static Rendering)。從Enzyme 3開始,在安裝Enzyme的同時,還需要安裝與React版本相對應的適配器,命令如下所示。 ~~~ npm install --save enzyme enzyme-adapter-react-16 ~~~ **1)淺層渲染** &emsp;&emsp;獨立于DOM的[淺層渲染](https://airbnb.io/enzyme/docs/api/shallow.html)只會渲染React組件的第一層,它會忽略子組件的行為,也就沒必要渲染子組件了,這提供了更好的隔離性。不過淺層渲染也有它局限性,即不支持Refs。 &emsp;&emsp;以上一節中的Link組件為例,在進行Enzyme之前,需要先通過configure()函數配置適配器,然后才能通過shallow()函數淺渲染Link組件,如下所示。 ~~~ import React from "react"; import { shallow, configure } from "enzyme"; import Adapter from "enzyme-adapter-react-16"; import Link from "../component/Form/Link"; configure({ adapter: new Adapter() }); test("Link changes the class after mouseenter", () => { const wrapper = shallow(<Link>Strick</Link>), a = wrapper.find("a"); expect(wrapper.text()).toEqual("Strick"); a.simulate("mouseenter");     //觸發事件 expect(a.prop("className")).toEqual("normal"); //匹配樣式 }); ~~~ &emsp;&emsp;wrapper是一個虛擬的DOM對象,它包含多個操作DOM的方法,例如find()可根據選擇器找到指定的節點,simulate()可觸發當前節點的事件。 **2)完全渲染** &emsp;&emsp;mount()函數會完全渲染接收的組件,即它的子組件也會被渲染。完全渲染依賴JSDOM,當多個測試處理同一個DOM時,可能會相互影響,因此在測試結束后需要使用unmount()方法卸載組件。 **3)靜態渲染** &emsp;&emsp;render()函數會靜態渲染組件,也就是將它渲染成HTML字符串,再通過Cheerio庫解析該HTML結構。Cheerio類似于JSDOM,但更輕量,可像jQuery那樣操作字符串。 ***** > 原文出處: [博客園-React躬行記](https://www.cnblogs.com/strick/category/1455720.html) [知乎專欄-React躬行記](https://zhuanlan.zhihu.com/pwreact) 已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。 ![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200) 推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看