單元測試反應元件
單元測試是一個很好的學科,可以導致生產蟲密度降低40%-80% 。單元測試還有其他幾個重要的好處:
但有些事情比其他事情更容易進行單元測試。具體來說,單元測試非常有用pure functions :給定相同輸入的函式,始終返回相同的輸出,並且沒有副作用。
通常,UI元件不屬於易於單元測試的那類事物,這使得更難堅持TDD的規則:首先編寫測試。
首先編寫測試對於實現我列出的一些好處是必要的:架構改進,更好的開發人員體驗設計以及在開發應用程式時更快的反饋。培訓自己使用TDD需要紀律和實踐。許多開發人員在編寫測試之前更喜歡修補,但如果你不先編寫測試,就會剝奪自己很多單元測試的最佳功能。
不過,值得練習和訓練。帶有單元測試的TDD可以訓練您編寫UI元件,這些元件更簡單,更易於維護,並且更易於與其他元件組合和重用。
我的測試學科最近的一項創新是開發RITEway單元測試框架 ,這是一個微小的包裝Tape 這有助於您編寫更簡單,更易於維護的測試。
無論您使用什麼框架,以下提示將幫助您編寫更好,更可測試,更易讀,更可組合的React元件:
純元件是一個元件,在給定相同的道具的情況下,它總是呈現相同的UI,並且沒有副作用。例如,
這些元件通常很容易測試。你需要一種方法來選擇元件(在這種情況下,我們選擇的是greeting
className
),你需要知道預期的輸出。要編寫純元件測試,我使用render-component
fromRITEway
.
要開始使用,請安裝RITEway:
在內部,RITEway使用react-dom/server
renderToStaticMarkup()
並將輸出包裝在一個Cheerio
物件易於選擇。如果您不使用RITEway,您可以手動執行所有操作以建立自己的函式,以將React元件呈現為可以使用Cheerio查詢的靜態標記。
一旦你有一個渲染函式從你的標記生成一個Cheerio物件,你可以編寫如下的元件測試:
但那不是很有趣。如果您需要測試有狀態元件或具有副作用的元件,該怎麼辦?這就是TDD對React元件非常有趣的地方,因為這個問題的答案與另一個重要問題的答案相同:“我怎樣才能使我的React元件更易於維護和除錯?”
答案:從您的簡報元件中隔離您的狀態和副作用。您可以通過將狀態和副作用管理封裝在容器元件中,然後通過props將狀態傳遞到純元件中來實現。
但是鉤子API是不是因為我們可以擁有扁平的元件層次結構而忘記所有元件巢狀的東西?嗯,不太好。將程式碼儲存在三個不同的儲存桶中仍然是一個好主意,並使這些儲存桶彼此隔離:
根據我的經驗,如果您將顯示/ UI問題與程式邏輯和副作用分開,它會讓您的生活更輕鬆。對於我來說,這個經驗法則始終適用於我曾經使用的每種語言和每個框架,包括React with hooks。
讓我們通過構建一個點選計數器來演示有狀態的元件。首先,我們將構建UI元件。它應該顯示類似“Clicks:13”的內容,告訴您單擊按鈕的次數。按鈕只會說“點選”。
顯示元件的單元測試非常簡單。我們真的只需要測試按鈕是否被渲染(我們不關心標籤所說的內容 - 根據使用者的語言環境設定,它可能會說不同語言中的不同內容)。我們do 想確保顯示正確的點選次數。讓我們編寫兩個測試:一個用於按鈕顯示,另一個用於正確重新插入的點選次數。
當使用TDD時,我經常使用兩個不同的斷言來確保我已經編寫了元件,以便從props中提取適當的值。可以編寫測試,以便您可以對函式中的值進行硬編碼。為了防範這種情況,您可以編寫兩個測試,每個測試測試不同的值。
在這種情況下,我們將建立一個名為的元件<ClickCounter>
,該元件將具有點選計數的道具,稱為clicks
。要使用它,只需渲染元件並設定clicks
支援您希望它顯示的點選次數。
讓我們看看一對單元測試,它們可以確保我們從道具中提取點選次數。讓我們建立一個新檔案,click-counter/click-counter-component.test.js
:
我喜歡建立一些小工廠函式,以便更容易編寫測試。在這種情況下,createCounter
將需要多次點選才能注入,並使用該點選次數返回呈現的元件:
通過編寫測試,是時候建立我們的ClickCounter
顯示元件。我用我的測試檔案在同一個資料夾中放置了我的名字,click-counter-component.js
。首先,讓我們編寫一個元件片段並觀察我們的測試失敗:
如果我們儲存並執行我們的測試,我們將得到一個TypeError
,目前觸發NodeUnhandledPromiseRejectionWarning
- 最終,Node將停止使用額外段落的惱人警告DeprecationWarning
and just throw anUnhandledPromiseRejectionError
相反。我們得到了TypeError
因為我們的選擇迴歸null
,我們正試圖跑.trim()
在上面。讓我們通過渲染預期的選擇器來解決這個問題:
大。現在我們應該有一個通過測試,一個失敗的測試:
要修復它,將計數作為道具,並使用JSX中的實時道具值:
現在我們的整個測試套件正在通過:
是時候測試按鈕了。首先,新增測試並觀察它失敗(TDD樣式):
這會導致測試失敗:
現在我們將實現點選按鈕:
測試通過:
現在我們只需要實現狀態邏輯並掛鉤事件處理程式。
我要向您展示的方法對於點選計數器可能有點過分,但大多數應用程式遠比點選計數器複雜。狀態通常儲存到資料庫或在元件之間共享。 React社群中流行的副詞是從本地元件狀態開始,然後根據需要將其提升到父元件或全域性應用程式狀態。
事實證明,如果使用純函式啟動本地元件狀態管理,則以後可以更輕鬆地管理該過程。由於這個和其他原因(如React生命週期混亂,狀態一致性,避免常見錯誤),我喜歡使用純reducer函式實現我的狀態管理。對於本地元件狀態,您可以匯入它們並應用useReducer
React hook.
如果您需要解除由Redux等州經理管理的狀態,那麼在您開始之前就已經有了一半:單元測試等等。
首先,我將為狀態縮減器建立一個新的測試檔案。我將它放在同一個資料夾中,但使用不同的檔案。我叫這個click-counter/click-counter-reducer.test.js
:
我總是從斷言開始,以確保reducer將產生有效的初始狀態。如果您以後決定使用Redux,它將呼叫每個reducer沒有狀態,以便為商店生成初始狀態。這也使得在您需要單元測試或初始化元件狀態時,可以非常輕鬆地建立有效的初始狀態。
當然,我們需要建立一個相應的reducer檔案。我在叫它click-counter/click-counter-reducer.js
:
我首先只是匯出一個空的reducer和action creator。要了解有關動作建立者和選擇者等重要角色的更多資訊,請閱讀“更好的Redux架構的10個技巧” 。我們現在不打算深入研究React / Redux架構模式,但是理解這個主題對於理解我們在這裡做的事情還有很長的路要走,即使你不打算使用Redux庫。
首先,我們將觀察測試失敗:
現在讓我們進行測試通過:
初始值測試現在將通過,但是是時候新增更有意義的測試了:
觀察測試失敗(兩者都返回0
什麼時候應該回來1
and4
, 分別)。然後實現修復。
請注意,我正在使用click()
action creator作為reducer的公共API。在我看來,您應該將reducer視為您的應用程式不直接與之互動的東西。相反,它使用動作建立器和選擇器作為reducer的公共API。
我也沒有為動作建立者和選擇者編寫單獨的單元測試。我總是和減速機一起測試它們。測試reducer是測試動作建立者和選擇器,反之亦然。如果您遵循此經驗法則,您將需要更少的測試,但仍然可以獲得與單獨測試時相同的測試和案例覆蓋率。
現在所有單元測試都將通過:
再多一步:將我們的行為連線到我們的元件。我們可以用容器元件來做到這一點。我會打電話給你index.js
並將其與其他檔案共存。它應該看起來像這樣:
而已。該元件唯一的工作是連線我們的狀態管理並將狀態作為道具傳遞給我們經過單元測試的純元件。要測試它,請在瀏覽器中載入應用程式,然後單擊單擊按鈕。
到目前為止,我們還沒有在瀏覽器中檢視元件或完成任何型別的樣式。為了澄清我們正在計算的內容,我將新增一個標籤和一些空間ClickCounter
零件。我也會聯絡上onClick
功能。現在程式碼看起來像這樣:
所有單元測試仍然通過。
那麼容器元件的測試怎麼樣?我沒有對容器元件進行單元測試。相反,我使用功能測試,它在瀏覽器中執行並模擬使用者與實際UI的互動,端到端執行。您需要在應用程式中進行兩種測試(單元和功能),並且對容器元件進行單元測試(主要是連線/接線元件,如上面連線減速器的那些元件)對於我的口味的功能測試來說太多餘了,並不是特別容易進行適當的單元測試。通常,您必須模擬各種容器元件依賴關係才能使它們工作。
與此同時,我們對所有不依賴於副作用的重要單元進行了單元測試:我們正在測試是否呈現了正確的資料以及正確管理狀態。您還應該在瀏覽器中載入元件,並親自檢視按鈕是否正常工作以及UI是否響應。
為React實現功能/ e2e測試與為任何其他框架實現它們相同。我不會在這裡討論它們,但請檢視TestCafe ,TestCafe Studio andCypress.io 沒有Selenium舞蹈的e2e測試。