原文:https://around25.com/blog/component-testing-using-react-native-testing-library/
譯者:明月
在本篇文章中,我們將探討 React Native 應用程式開發生態系統中的單元測試基礎知識。首先,讓我們了解一下什麼是單元測試以及為什麼在任何軟體開發生命周期中都需要用到它。在單元測試中,項目中的每個組件都作為一個單元進行單獨測試。這樣就可以測試獨立的組件,直到它們滿足設計和功能規範要求。主要思想是弄清楚該軟體的每個組件是否按照預期工作。僅當軟體的每個單元正常工作時,環境才能有效地工作並按預期運行。
作為開發人員,您可以進行單元測試,也可以由獨立的測試人員執行。
測試初學者的注意事項:單元測試可以是手動的也可以是自動的。整個過程涉及創建測試用例,對這些用例進行審查,重新處理然後執行。
單元測試作為功能實現。通過引用各種輸入值來檢查功能的輸出。這使調試過程變得更加容易。
整個軟體開發過程變得敏捷:引入任何新的修改後,就可以單獨測試組件並將其集成到系統中,從而節省大量時間和成本。
由於可以在早期階段檢測和管理漏洞,因此減少了最後獲得煩人bug的機會。整體重構和更新變得更加方便。
由於您已經進行了單元測試,可以為每個模塊及其功能提供深入的概述,因此文檔將變得不再那麼繁瑣。
整個代碼成為模塊化的。這將是代碼變得可靠和可重用。
它有助於從統計角度衡量每個組件和整個系統的性能,同時還能確定組件在系統中覆蓋的區域。
您避免交付容易出錯的產品。另外,您可以在降低複雜性的同時提高代碼質量。
總體而言,單元測試是交付成功產品的最安全方法之一。
React Native測試庫 提供了簡單而完整的 React Native測試實現程序。
這是用於測試React Native組件的輕量級解決方案,它在react-test-renderer之上促進了實用方法。這將鼓勵更好的測試實踐。該庫與Jest並駕齊驅,但也可以與其他測試運行者合作。
為為了在React Native 項目中進行測試,我們首先需要一個入門項目,您可以繼續從snack項目中下載並啟動項目。下載後,我們需要通過執行以下命令在仿真器或連接的設備上運行項目:
在應用程式中,我們有一個輸入欄位,一個用於添加項目的按鈕和一個列表。該列表包含一個刪除按鈕,用於刪除列表項。主頁分為三個部分:
主主屏幕(Home.js)
列表組件(ItemsList.js)
添加項目組件(ItemAdder.js)
要執行單元測試,我們需要將React Native測試庫安裝到我們的項目中。為此,我們需要在項目終端中運行以下命令:
npm install --save-dev @testing-library/react-native該庫有一個react-test-renderer的peerDependencies列表。
對於測試人員,我們將在這裡使用 jest。因此,我們需要安裝jest-native庫作為附加的jest匹配器。為此,我們需要在項目終端中運行以下命令:
npm install --save-dev @testing-library/jest-native現在,我們需要配置我們的jest設置文件,即package.json文件中的模式。為此,我們在package.json文件中存在的jest對象中添加以下配置行:
{ "jest": { "preset": "react-native", "setupFilesAfterEnv": ["@testing-library/jest-native/extend-expect"], "transformIgnorePatterns": ["node_modules/(?!(jest-)?react-native|@?react-navigation)"], "setupFiles": ["./node_modules/react-native-gesture-handler/jestSetup.js"] }}我們已完成配置,但未執行測試。
因此,我們在一個單獨的文件夾中創建一個新的測試文件,我們將在項目的./screens目錄中調用./test。在其中,我們創建一個名為Home.test.js的文件,該文件將包含與主屏幕有關的所有測試,並在運行測試命令後執行。
現在是時候將所需的庫導入Home.test.js文件中以進行測試了:
import React from 'react';import {render, fireEvent} from '@testing-library/react-native';import Home from '../Home';為了測試基本的主屏幕模板,我們將主屏幕模板代碼渲染到終端中。
如果一切正確呈現,那就太好了!沒有問題。現在使用render()方法並將Home組件傳遞給它。
渲染方法將為我們提供測試主屏幕內部組件所需的方法。
同樣,我們在這裡使用debug方法來呈現主屏幕模板:
test('rendering Home component', async () => { const {debug} = render(<Home/>); debug();});此時,我們可以使用兩個參數來實現測試功能,第一個參數是測試的名稱,第二個參數是在執行測試時觸發的回調函數。在package.json文件中,我們有一個腳本來運行測試:"scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", "test": "jest", "lint": "eslint ."}每次我們要執行測試時,都需要在項目終端中運行以下命令:
因此,我們將獲得主屏幕模板的完整模板渲染:
如果發生任何錯誤,則模板JSX元素的集成可能存在問題,該問題將在錯誤消息本身中指示。
主屏幕中的輸入欄位和按鈕組件是從ItemAdder組件導入的。
要測試TextInput和Button,我們需要為其分配testID屬性。使用這些testID道具,我們將能夠在測試配置中掌握這些元素。
<TextInput value={input} onChangeText={setInput} placeholder="Add Item..." style={styles.input} testID={`${testID}-input`}/><Button color="#000" testID = "input-button" title="+ ADD" style = {styles.button} onPress={() => { addItem(input); setInput(''); }}/>現在,我們可以使用Home.test.js文件中的getByTestId方法訪問這些輸入和按鈕。然後我們通過提及它們的testID來初始化輸入欄位和按鈕:
test('testing input and button', async () => { const {getByTestId, getByText} = render( <Home />, );
const input = getByTestId('adder-input'); const button = getByTestId('input-button');
});接下來,我們使用從測試庫導入的fireEvent對象實例,應用輸入並觸發按鈕單擊。我們可以在輸入中輸入文本值,並以此觸發按鈕的按下:
fireEvent.changeText(input, 'element');fireEvent.press(button);在這裡,我們使用fireEvent類中的changeText方法將輸入提供給我們應用程式中的TextInput元素。我們提供的輸入值為'element'。按下功能將觸發我們應用中的「添加」按鈕,這將使輸入顯示在列表中。
問題是:如何觸發觸發事件後,輸入值「 element」是否已出現在列表中?
我們可以檢查渲染中是否存在文本值「 element」。
為此,我們可以利用render()提供的getByText方法。如果「元素」在渲染器中可用,則將其分配給元素常量。該代碼在下面的代碼片段中提供:
const element = getByText('element');現在,我們需要檢查元素是否存在於渲染器中。在其他情況下,我們期望element值出現在render中。
這使我們可以使用Expect函數中的tobeDefined方法,該方法將元素作為參數。如果存在該值,則不會發生錯誤。否則,將發生錯誤。
expect(element).toBeDefined();檢查以下完整的測試功能:
test('testing input and button', async () => { const {getByText, getByTestId} = render( <Home />, );
const input = getByTestId('adder-input'); const button = getByTestId('input-button'); fireEvent.changeText(input, 'element'); f ireEvent.press(button); const element = getByText('element'); expect(element).toBeDefined();});在這裡,我們希望在添加輸入時定義元素並獲得此測試結果:
我們可以看到測試已經通過。
這就是為什麼我們現在也可以檢查多個輸入的原因。為此,我們分配了一個不同的輸入值'element1',並像之前的測試一樣進行了所有操作。函數是這樣的:
test('testing input and button', async () => {
const {getByText, getByTestId} = render( <Home />, );
const input = getByTestId('adder-input'); const button = getByTestId('input-button'); fireEvent.changeText(input, 'element'); fireEvent.press(button); const element = getByText('element'); expect(element).toBeDefined();
fireEvent.changeText(input, 'element1'); fireEvent.press(button); const element1 = getByText('element1'); expect(element1).toBeDefined();});如果運行測試命令,則會得到以下結果:
在應用程式演示中,您會注意到,當我們將項目添加到列表中時,我們還會獲得一個刪除按鈕。
當我們按下它時,該項目消失。要測試按鈕,我們需要將testID屬性分配給ItemList.js文件中的delete Button組件:
<Button title="Delete" onPress={() => deleteItem(value)} color="#f12" testID="delete-list-item"/>現在在Home.test.js中,我們需要創建一個稱為「 testing delete」的新測試函數。
要測試刪除按鈕,首先我們必須在列表中有一個項目。
因此,我們將使用與以前相同的編碼實現來輸入項目,並檢查其渲染。然後,我們使用getAllByTestID方法,該方法將每個元素以及在其中定義的特定testID定義為數組。
現在,將數組分配給deleteButton常量:我們像以前一樣使用fireEvent類中的press方法觸發按鈕。但是,由於我們僅刪除列表的第一項,因此我們還需要分配數組標識符。這將導致列表中的第一項被刪除。
因此,我們使用queryByText檢查該第一項在渲染中是否可用。然後,通過應用toBeNull從方法期望的功能,我們可以檢查數組的第一個項目是否可用或者不渲染。在這裡獲取功能:
test('testing delete', async () => {
const {getByText, getByTestId, getAllByTestId, queryByText} = render( <Home />, );
const input = getByTestId('adder-input'); const button = getByTestId('input-button'); fireEvent.changeText(input, 'element'); fireEvent.press(button); const element = getByText('element'); expect(element).toBeDefined();
const deleteButton = getAllByTestId('delete-list-item'); fireEvent.press(deleteButton[0]); expect(queryByText('element')).toBeNull(); });我們在這裡所做的是,我們使用tobeNull方法將'element'值添加到列表中,然後將其刪除並檢查是否刪除了'element' 。
如果答案是正確的,則該元素將被刪除,並且刪除功能將起作用。測試也應該成功。現在,如果運行測試,我們將得到以下結果:
如我們所見,我們對刪除按鈕的測試也成功。
現在,對於下一個測試,我們在輸入值並單擊「添加」按鈕後檢查列表項是否可用。
test('testing list items', async () => {
const {getByText, getByTestId} = render( <Home />, );
const input = getByTestId('adder-input'); const button = getByTestId('input-button');
fireEvent.changeText(input, 'element'); fireEvent.press(button); const element = getByText('element'); expect(element).toBeDefined();
fireEvent.changeText(input, 'element1'); fireEvent.press(button); const element1 = getByText('element1'); expect(element1).toBeDefined();
const listElements = getByTestId("list"); expect(listElements).toContainElement(element);});所有步驟,不同的類似,我們使用toContainElement從期望函數來檢查我們在列表中輸入特定的產品存在。因此,我們應該得到以下結果:
通過準備不同的測試功能並分配測試ID來渲染元素,我們可以測試其他組件。
哇,很長的一段路程。讓我們快速回顧一下我們學到的東西:
最重要的一點是:通過基於組件渲染編寫這些類型的測試,我們可以驗證React Native應用程式的各個組件。如果發生任何錯誤,這將有助於優化應用程式。
測試庫提供了其他API和方法,您可以自由探索。面臨的挑戰是為各個組件設計自己的測試。您甚至可以先創建一個組件,然後按上述步驟對其進行測試,然後再將其集成到核心應用中。這稱為測試驅動開發。我們僅用幾行代碼即可執行測試的簡單性令人驚嘆。
最後希望您能夠使用 React Native Testing 庫在React Native中掌握基於組件的基本測試方法。
以上對單元測試的原理進行了詳細的闡述,並通過 React Native Testing 庫結合具體的案例進行了詳細的代碼輸出 ,如果您認為對您有幫助,請記得 點讚、留言、分享和收藏 哦~~