自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

使用Enzyme測試React(Native)組件

開發(fā) 開發(fā)工具
我們非常享受Enzyme為React.js應用提供的快速組件級UI測試功能。與許多其他基于快照的測試框架不同,Enzyme允許開發(fā)者在不進行設備渲染的情況下做測試,從而實現(xiàn)速度更快、粒度更小的測試。

一、組件化與UI測試

在組件化出現(xiàn)之前,我們不談UI的單元測試,哪怕是對于UI頁面進行測試都是一件非常困難的事情。其實組件化并不完全是為了復用,很多情況下也恰恰是為了分治,使得我們可以分組件對UI頁面進行開發(fā),然后分別對其進行單元測試。

特別是當瀏覽器中的Web應用越來越龐大的時候,與在后端將大型單體應用拆分成微服務架構的***實踐一樣,前端應用也可以被拆分成不同的頁面和特性。

[[186101]]

每個特性由一個單獨的團隊從端到端對其負責,它允許團隊規(guī)?;亟桓赌切┠軌颡毩⒉渴鸷途S護的服務,在2016年11月期的技術雷達當中這種方式被稱之為微前端,微前端的目標就是允許Web應用的特性彼此獨立,每個特性可以獨立地開發(fā)、測試和部署。

React.js作為前端框架的后起之秀,卻在2015年攜著虛擬DOM、組件化、單向數(shù)據(jù)流等利器,給前端UI構建掀起了一波聲勢浩大的函數(shù)式新潮流。

雖說組件化不是React***提出來的,但卻是被React在前端世界里發(fā)揚光大的,而現(xiàn)在幾乎所有的所謂現(xiàn)代化UI框架比如Angular或者Vue都已經(jīng)將組件化作為框架的立足之本。

[[186102]]

React已經(jīng)讓UI測試變得容易很多,React組件都可以被簡化為這樣一個表達式,即UI=f(data),這個純函數(shù)返回的只是一個描述UI組件應該是什么樣子的虛擬DOM,本質(zhì)上就是一個樹形的數(shù)據(jù)結構。給這個純函數(shù)輸入一些應用程序的狀態(tài),就會得到相應的UI描述的輸出,這個過程不會去直接操作實際的UI元素,也不會產(chǎn)生所謂的副作用。

二、React組件樹的測試

按理來說按照純函數(shù)這樣的思路,React組件的測試應該很簡單。但與此同時,對于(渲染出UI的)組件樹進行測試依然存在一個問題,從下圖中可以看出,越處于上層的組件,其復雜度越高。

對于***層的子組件來說,我們可以很容易的將其進行渲染并測試其邏輯正確與否,但對于較上層的父組件來說,就需要對其所包含的所有子組件都進行預先渲染,甚至于最上面的組件需要渲染出整個 UI 頁面的真實DOM節(jié)點才能對其進行測試,這顯然是不可取的。

React組件樹

Shallow rendering lets you render a component "one level deep" and assert facts about what its render method returns, without worrying about the behavior of child components, which are not instantiated or rendered. This does not require a DOM.

淺渲染(Shallow Rendering)解決了這個問題,也就是說在我們針對某個上層組件進行測試時,可以不用渲染它的子組件,所以就不用再擔心子組件的表現(xiàn)和行為,這樣就可以只對特定組件的邏輯及其渲染輸出進行測試了。Facebook官方提供了react-addons-test-utils可以讓我們使用淺渲染這個特性,用于測試虛擬DOM對象,即React.Component的實例。

三、使用Enzyme簡化測試代碼

我們常常會提到,測試代碼對于復雜代碼庫的可維護性至關重要,但是測試代碼本身的易于理解和編寫,以及可讀性和可維護性也同等重要。

Enzyme is a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components' output.

而Enzyme則來自于活躍在JavaScript開源社區(qū)的Airbnb公司,是對官方測試工具庫(react-addons-test-utils)的封裝,它模擬了jQuery的API,非常直觀并且易于使用和學習,提供了一些與眾不同的接口和方法來減少測試的樣板代碼,方便你判斷、操縱和遍歷React Components的輸出,并且減少了測試代碼和實現(xiàn)代碼之間的耦合。

Enzyme理論上應該與所有TestRunner和斷言庫相兼容,已經(jīng)集成了多種測試類庫,比如Jest、Mocha&Chai、Jasmine,不過這些不是我們今天的重點。

對比一下兩者facebook/react-addons-test-utils vs airbnb/enzyme的API就一目了然,立見分明:

facebook/react-addons-test-utils vs airbnb/enzyme的API

四、Enzyme的三種渲染方法

1. shallow(node[, options]) => ShallowWrapper

shallow方法就是對官方的Shallow Rendering的封裝,淺渲染在將一個組件作為一個單元進行測試的時候非常有用,可以確保你的測試不會去間接斷言子組件的行為。shallow方法只會渲染出組件的***層DOM結構,其嵌套的子組件不會被渲染出來,從而使得渲染的效率更高,單元測試的速度也會更快。

  1. import { shallow } from 'enzyme' 
  2. describe('Enzyme Shallow', () => { 
  3.   it('App should have three <Todo /> components', () => { 
  4.    const app = shallow(<App />
  5.    expect(app.find('Todo')).to.have.length(3) 
  6.   }) 

2. mount(node[, options]) => ReactWrapper

mount方法則會將React組件渲染為真實的DOM節(jié)點,特別是在你依賴真實的DOM結構必須存在的情況下,比如說按鈕的點擊事件。

完全的DOM渲染需要在全局范圍內(nèi)提供完整的DOM API,這也就意味著它必須在至少“看起來像”瀏覽器環(huán)境的環(huán)境中運行,如果不想在瀏覽器中運行測試,推薦使用mount的方法是依賴于一個名為jsdom的庫,它本質(zhì)上是一個完全在JavaScript中實現(xiàn)的headless瀏覽器。

  1. import { mount } from 'enzyme' 
  2. describe('Enzyme Mount', () => { 
  3.   it('should delete Todo when click button', () => { 
  4.    const app = mount(<App />
  5.    const todoLength = app.find('li').length 
  6.    app.find('button.delete').at(0).simulate('click') 
  7.    expect(app.find('li').length).to.equal(todoLength - 1) 
  8.   }) 
  9. }) 

3. render(node[, options]) => CheerioWrapper

render方法則會將React組件渲染成靜態(tài)的HTML字符串,返回的是一個Cheerio實例對象,采用的是一個第三方的HTML解析庫Cheerio,官方的解釋是「我們相信Cheerio可以非常好地處理HTML的解析和遍歷,再重復造輪子只能算是一種損失」。

  1. import { render } from 'enzyme' 
  2. describe('Enzyme Render', () => { 
  3.   it('Todo item should not have todo-done class', () => { 
  4.    const app = render(<App />
  5.    expect(app.find('.todo-done').length).to.equal(0) 
  6.    expect(app.contains(<div className="todo" />)).to.equal(true) 
  7.   }) 
  8. }) 

 

這個CheerioWrapper可以用于分析最終結果的HTML代碼結構,它的API跟shallow和mount方法的API都保持基本一致。

五、Enzyme 的 API 方法

1. find() 方法與選擇器

從前面的示例代碼中可以看到,無論哪種渲染方式所返回的wrapper都有一個.find()方法,它接受一個selector參數(shù),然后返回一個類型相同的wrapper對象,里面包含了所有符合條件的子組件。在這個對象的基礎上,at方法則可以返回指定位置的子組件,simulate方法可以在這個組件上模擬觸發(fā)某種行為。

Enzyme中的Selectors即選擇器類似于CSS選擇器,但是只支持非常簡單的CSS選擇器,如果需要支持復雜的CSS選擇器,就需要引入react-dom模塊的findDOMNode方法,而這是官方的TestUtils都無法提供的方式。

  1. /* CSS Selector */ 
  2. wrapper.find('.foo') //class syntax 
  3. wrapper.find('input') //tag syntax 
  4. wrapper.find('#foo') //id syntax 
  5. wrapper.find('[htmlFor="foo"]') //prop syntax 

Selectors也可以是許多其他的東西,以便于在Enzyme的wrapper中輕松地指定想要查找的節(jié)點,在下面的示例中,我們可以通過React組件構造函數(shù)的引用找到該組件,也可以基于React的displayName來查找組件。

  1. /* Component Constructor */ 
  2. wrapper.find(ChildrenComponent) 
  3. myComponent.displayName = 'ChildrenComponent' 
  4. wrapper.find('ChildrenComponent') 
  5. /* Object Property Selector */ 
  6. const wrapper = mount
  7.   <div> 
  8.    <span foo={3} bar={false} title="baz" /> 
  9.   </div> 
  10. wrapper.find({ foo: 3 }) 
  11. wrapper.find({ bar: false }) 
  12. wrapper.find({ title: 'baz'}) 

 

如果一個組件存在于渲染樹中,其中設置了displayName并且它的***個字符為大寫字母,就能通過字符串找到它,與此同時也可以基于React組件屬性的子集來查找組件和節(jié)點。

2. 測試組件的交互行為

我們不但可以通過find方法查找DOM元素,還可以通過simulate方法在組件上模擬觸發(fā)某個DOM事件,比如Click,Change等等。

對于淺渲染來說,事件模擬并不會像真實環(huán)境中所預期的那樣進行傳播,因此我們必須在一個已經(jīng)設置好了事件處理方法的實際節(jié)點上調(diào)用,實際上.simulate()方法將會根據(jù)模擬的事件觸發(fā)這個組件的prop。例如,.simulate('click') 實際上會獲取onClick prop并調(diào)用它。

  1. it('simulates click events', () => {  
  2.   const onButtonClick = sinon.spy() 
  3.   const wrapper = shallow
  4.    <Foo onButtonClick={onButtonClick} /> 
  5.   ) 
  6.   wrapper.find('button').simulate('click') 
  7.   expect(onButtonClick.calledOnce).to.be.true 
  8. }) 

Sinon則是一個可以用來Mock和Stub數(shù)據(jù)代碼的第三方測試工具庫,當我們需要檢查一個組件當中某個特定的函數(shù)是否被調(diào)用時,我們可以使用sinon.spy()方法監(jiān)視所傳入該組件作為prop的onButtonClick方法,然后再通過wrapper的simulate方法模擬一個Click事件,最終驗證這個被spy的onButtonClick函數(shù)是否被調(diào)用。

六、如何測試 React Native?

前面我們所談論的都是如何測試使用react-dom所構建的React組件,即最終渲染的結果是瀏覽器當中的DOM結構,但對于React Native來說,JavaScript代碼最終會被編譯并用于調(diào)用iOS或Android上的Native代碼,因此無法再使用基于DOM的測試工具了。

[[186103]]

與此同時,React Native還有特別多的Mobile環(huán)境依賴,所以在沒有真實設備的情況下很難對其運行環(huán)境進行模擬,特別是當你希望在持續(xù)集成服務器(如Jenkins、Travis CI)運行單元測試的時候。

事實上,我們可以通過欺騙React Native讓它返回常規(guī)的React組件而不是Native組件,然后就又能愉快地使用傳統(tǒng)的JavaScript測試庫來單獨測試React Native組件邏輯。最基本的mock示例代碼如下:

  1. const mockComponent = (type) => { 
  2.   return React.createClass({ 
  3.    displayName: type, 
  4.    propTypes: { 
  5.      children: React.PropTypes.node 
  6.    }, 
  7.    render() { 
  8.      return <div {...this.props}>{this.props.children}</div> 
  9.    } 
  10.   }) 
  11. RN.View = mockComponent("View") 
  12. RN.Text = mockComponent("Text") 
  13. RN.Image = mockComponent("Image") 

 

Enzyme推薦在測試環(huán)境中使用react-native-mock這個輔助庫,這是一個使用純JavaScript將全部的React Native組件進行mock的第三方庫,只需要導入這個庫就可以對React Native組件進行渲染和測試。

七、總結

我們非常享受Enzyme為React.js應用提供的快速組件級UI測試功能。與許多其他基于快照的測試框架不同,Enzyme允許開發(fā)者在不進行設備渲染的情況下做測試,從而實現(xiàn)速度更快、粒度更小的測試。在開發(fā)React應用時,我們經(jīng)常需要做大量的功能測試,而Enzyme可以在大規(guī)模地減少功能測試數(shù)量上做出貢獻。

Enzyme

【本文是51CTO專欄作者“ThoughtWorks”的原創(chuàng)稿件,微信公眾號:思特沃克,轉(zhuǎn)載請聯(lián)系原作者】

戳這里,看該作者更多好文

 

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2023-11-29 11:31:03

2025-02-06 03:15:48

2018-06-13 16:38:33

React Nativ組件Android

2016-08-31 17:03:20

JavascriptReact NativWeb

2016-10-13 19:01:59

React NativUbuntu

2025-04-18 16:05:39

2016-08-12 13:55:06

2023-06-24 17:09:06

React前端

2024-07-08 00:00:07

2022-07-18 09:01:58

React函數(shù)組件Hooks

2016-11-09 12:18:00

ui動畫javascript

2021-07-25 21:36:24

Windows操作系統(tǒng)功能

2016-08-12 08:49:46

React NativFacebookNative

2015-09-22 09:50:36

FacebookAndroid

2017-09-11 14:35:34

編輯器開發(fā)環(huán)境React

2016-08-15 13:34:37

React NativiOSjs入口

2017-03-09 13:29:04

ReactNative JSPatch

2024-01-19 09:03:06

ReactTypeScripFlexbox

2024-02-20 01:53:01

ReactFlutter開發(fā)

2022-04-14 08:00:00

Cypress測試開發(fā)
點贊
收藏

51CTO技術棧公眾號