天天看點

jest測試react元件_如何使用Jest測試React元件

jest測試react元件

本文由特邀作者傑克·富蘭克林 ( Jack Franklin)發表 。 SitePoint訪客文章旨在為您帶來來自JavaScript社群的傑出作家和演講者的引人入勝的内容。

在本文中,我們将研究如何使用Jest (Facebook維護的測試架構)來測試ReactJS元件。 我們将首先研究如何在普通JavaScript函數上使用Jest,然後再檢視Jest提供的一些特定功能,這些功能專門旨在簡化React應用程式的測試。 值得注意的是,Jest并不是專門針對React的:您可以使用它來測試任何JavaScript應用程式。 但是,它提供的一些功能非常友善測試使用者界面,這就是為什麼它非常适合React的原因。

jest測試react元件_如何使用Jest測試React元件

樣品申請

在測試任何東西之前,我們需要一個應用程式進行測試! 忠于Web開發傳統,我建構了一個小型的todo應用程式,我們将以此為起點。 您可以在GitHub上找到它以及我們将要編寫的所有測試。 如果您想使用該應用程式來體驗一下它,還可以線上找到實時示範 。

該應用程式以ES2015編寫,并使用Webpack以及Babel ES2015和React預設進行編譯。 我不會詳細介紹建構設定,但是如果您想檢視的話,所有内容都在GitHub存儲庫中 。 您可以在自述檔案中找到有關如何使應用程式在本地運作的完整說明。 如果您想了解更多資訊,請使用Webpack建構該應用程式,并且我建議使用“ Webpack 入門指南 ”作為對該工具的很好介紹。

該應用程式的入口點是

app/index.js

,它僅将

Todos

元件呈現為HTML:

render(
  <Todos />,
  document.getElementById('app')
);
           

Todos

元件是應用程式的主要中心。 它包含所有狀态(此應用程式的寫死資料,實際上可能來自API或類似資料),并具有呈現兩個子元件的代碼:

Todo

,該狀态中的每個todo都會呈現一次,和

AddTodo

,後者呈現一次,并為使用者提供添加新待辦事項的表單。

因為

Todos

元件包含所有狀态,是以隻要有任何更改,它都需要

Todo

AddTodo

元件來通知它。 是以,它将功能向下傳遞給這些元件,當某些資料發生更改時可以調用這些元件,并且

Todos

可以相應地更新狀态。

最後,目前,您會注意到所有業務邏輯都包含在

app/state-functions.js

export function toggleDone(state, id) {…}

export function addTodo(state, todo) {…}

export function deleteTodo(state, id) {…}
           

這些都是純函數,它們接受狀态和一些資料,并傳回新狀态。 如果您不熟悉純函數,則它們是僅引用給定資料且沒有副作用的函數。 有關更多資訊,您可以閱讀我的純函數清單中的 文章以及關于純函數和React的SitePoint上的文章 。

如果您熟悉Redux,它們與Redux稱為reducer的内容非常相似。 實際上,如果此應用程式變得更大,我會考慮遷移到Redux以使用更明确,結構化的資料方法。 但是對于這種規模的應用程式,您通常會發現本地元件狀态和一些抽象功能足夠了。

去TDD還是不去TDD?

有許多關于測試驅動開發的優缺點的文章,其中要求開發人員首先編寫測試,然後再編寫修複測試的代碼。 其背後的想法是,通過首先編寫測試,您必須考慮正在編寫的API,它可以導緻更好的設計。 對于我來說,我發現這很大程度上取決于個人喜好以及我正在測試的東西。 我發現,對于React元件,我喜歡先編寫元件,然後将測試添加到最重要的功能中。 但是,如果您發現首先為元件編寫測試适合您的工作流程,則應該這樣做。 這裡沒有硬性規定。 做對您和您的團隊最有利的事情。

請注意,本文将重點介紹測試前端代碼。 如果您正在尋找後端方面的内容,請務必在Node.js中檢視SitePoint的課程“ 測試驅動開發” 。

開玩笑

Jest于2014年首次釋出,盡管最初引起了很多興趣,但該項目暫時處于Hibernate狀态,是以并未積極開展。 但是,Facebook在去年投入了資金來改善Jest,并在最近釋出了一些具有令人印象深刻的變化的版本,使其值得重新考慮。 與最初的開源版本相比,Jest的唯一相似之處在于名稱和徽标。 其他所有内容均已更改并重寫。 如果您想了解更多相關資訊,可以閱讀Christoph Pojer的評論 ,他在其中讨論了項目的目前狀态。

如果您對使用其他架構設定Babel,React和JSX測試感到沮喪,那麼我絕對建議您嘗試一下Jest。 如果您發現現有的測試設定很慢,我也強烈推薦Jest。 它會自動并行運作測試,并且其監視模式隻能運作與更改後的檔案相關的測試,這在您擁有大量測試時非常寶貴。 它配置了JSm ,這意味着您可以編寫浏覽器測試,但可以通過Node運作它們,可以處理異步測試,并具有内置的模拟,間諜和存根等進階功能。

安裝和配置Jest

首先,我們需要安裝Jest。 因為我們也在使用Babel,是以我們将安裝另外兩個子產品,這些子產品可以使Jest和Babel很好地發揮作用:

npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest
           

您還需要将

.babelrc

檔案與Babel配置為使用所需的任何預設和插件。 示例項目已經有此檔案,如下所示:

{
  "presets": ["es2015", "react"]
}
           
jest測試react元件_如何使用Jest測試React元件
jest測試react元件_如何使用Jest測試React元件

免費學習PHP!

全面介紹PHP和MySQL,進而實作伺服器端程式設計的飛躍。

原價$ 11.95 您的完全免費

免費獲得這本書

我們不會安裝任何React測試工具,因為我們不會從測試元件開始,而是測試狀态功能。

Jest希望在

__tests__

檔案夾中找到我們的測試,該檔案夾已成為JavaScript社群中流行的慣例,這是我們要堅持使用的檔案夾。 如果您不

__tests__

設定,那麼開箱即用的Jest也支援查找任何

.test.js

.spec.js

檔案。

在測試狀态函數時,請繼續建立

__tests__/state-functions.test.js

我們将在短期内編寫一個适當的測試,但現在,請進行此虛拟測試,這将使我們檢查所有工作是否正常,并配置了Jest。

describe('Addition', () => {
  it('knows that 2 and 2 make 4', () => {
    expect(2 + 2).toBe(4);
  });
});
           

現在,進入您的

package.json

。 我們需要設定

npm test

使其運作Jest,我們可以簡單地通過将

test

腳本設定為運作

jest

來做到這一點。

"scripts": {
  "test": "jest"
}
           

如果現在在本地運作

npm test

,則應該看到您的測試正在運作并通過!

PASS  __tests__/state-functions.test.js
  Addition
    ✓ knows that 2 and 2 make 4 (5ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 passed, 0 total
Time:        3.11s
           

如果您曾經使用過Jasmine或大多數測試架構,則上面的測試代碼本身應該非常熟悉。 Jest讓我們使用

describe

it

需要嵌套測試。 您使用多少嵌套取決于您自己; 我喜歡窩煤礦是以,所有傳遞給描述字元串

describe

it

幾乎讀作一個句子。

在進行實際的斷言時,您可以将要測試的内容包裝在

expect()

調用中,然後再在其上調用斷言。 在這種情況下,我們曾經使用過

toBe

。 您可以在Jest文檔中找到所有可用斷言的清單。

toBe

使用

===

來檢查給定值是否與被測值比對。 通過本教程,我們将滿足Jest的一些斷言。

測試業務邏輯

現在我們已經看到了Jest在虛拟測試上的工作,讓我們在真實的測試上運作它! 我們将測試我們的第一個狀态函數

toggleDone

toggleDone

擷取我們想要切換的待辦事項的目前狀态和ID。 每個待辦事項都有一個

done

屬性,并且

toggleDone

應該将其從

true

交換為

false

,反之亦然。

如果要繼續進行此操作,請確定已克隆存儲庫并将

app

檔案夾複制到包含

___tests__

檔案夾的同一目錄中。 您還需要安裝

shortid

軟體包(

npm install shortid --save

),它是Todo應用程式的依賴項。

我将從

app/state-functions.js

導入函數開始,并設定測試的結構。 盡管Jest允許您使用

describe

并且

it

可以根據需要深度嵌套,但是您也可以使用

test

,它通常會更好地讀取。

test

隻是Jest的

it

函數的别名,但是有時可以使測試更易于閱讀且嵌套更少。

例如,這裡是我會怎麼寫測試與嵌套的

describe

it

呼籲:

import { toggleDone } from '../app/state-functions';

describe('toggleDone', () => {
  describe('when given an incomplete todo', () => {
    it('marks the todo as completed', () => {
    });
  });
});
           

這就是我将如何通過

test

import { toggleDone } from '../app/state-functions';

test('toggleDone completes an incomplete todo', () => {
});
           

測試仍然可以很好地閱讀,但是現在縮進的方式越來越少了。 這主要取決于個人喜好; 選擇您更喜歡的風格。

現在我們可以寫斷言了。 首先,我們将建立開始狀态,然後将其傳遞到

toggleDone

以及要切換的待辦事項的ID。

toggleDone

将傳回完成狀态,然後可以對以下狀态進行斷言:

const startState = {
  todos: [{ id: 1, done: false, name: 'Buy Milk' }]
};

const finState = toggleDone(startState, 1);

expect(finState.todos).toEqual([
  { id: 1, done: true, name: 'Buy Milk' }
]);
           

現在注意,我使用

toEqual

進行斷言。 您應該使用

toBe

上的原始值,如字元串和數字,但

toEqual

對象和數組。

toEqual

用于處理數組和對象,并将遞歸檢查給定對象中的每個字段或項,以確定它們比對。

這樣,我們現在可以運作

npm test

并檢視狀态函數測試通過:

PASS  __tests__/state-functions.test.js
  ✓ tooggleDone completes an incomplete todo (9ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 passed, 0 total
Time:        3.166s
           

重新測試變更

對測試檔案進行更改,然後必須再次手動運作

npm test

,這有點令人沮喪。 Jest的最佳功能之一是其監視模式,該模式監視檔案更改并相應地運作測試。 它甚至可以根據更改的檔案找出要運作的測試子集。 它功能強大且可靠,您可以在監視模式下運作Jest,并在編寫代碼的過程中将其整日保留。

要在監視模式下運作它,可以運作

npm test -- --watch

。 您傳遞給任何

npm test

後的第一次

--

将直通到下面的指令來傳遞。 這意味着這兩個指令實際上是等效的:

  • npm test -- --watch

  • jest --watch

我建議您在本教程的其餘部分中讓Jest在另一個頁籤或終端視窗中運作。

在繼續測試React元件之前,我們将在另一個狀态函數上再編寫一個測試。 在實際的應用程式中,我将編寫更多的測試,但是出于教程的考慮,我将跳過其中的一些測試。 現在,讓我們編寫一個測試來確定我們的

deleteTodo

函數正常工作。 在檢視我如何在下面編寫它之前,請嘗試自己編寫并檢視您的測試比較。

給我看測試

請記住,你将不得不更新

import

在頂部import語句

deleteTodo

沿

toggleTodo

import { toggleTodo, deleteTodo } from '../app/state-functions';
           

這是我編寫測試的方式:

test('deleteTodo deletes the todo it is given', () =&gt; {
    const startState = {
      todos: [{ id: 1, done: false, name: 'Buy Milk' }]
    };

    const finState = deleteTodo(startState, 1);

    expect(finState.todos).toEqual([]);
  });
           

測試與第一個測試相差不大:我們設定了初始狀态,運作了函數,然後對完成狀态進行了斷言。 如果您讓Jest在監視模式下運作,請注意它如何拾取并運作新測試,以及這樣做的速度! 這是在編寫測​​試時獲得有關測試的即時回報的好方法。

上面的測試還示範了測試的理想布局,即:

  • 建立
  • 執行被測功能
  • 确定結果。

通過以這種方式安排測試,您會發現它們更易于遵循和使用。

現在我們很高興測試狀态函數,讓我們繼續使用React元件。

測試React元件

值得注意的是,預設情況下,我實際上鼓勵您不要在React元件上編寫太多測試。 任何您想進行徹底測試的内容(例如業務邏輯)都應從元件中拉出,并置于獨立的功能中,就像我們之前測試的狀态功能一樣。 也就是說,有時測試一些React互動非常有用(例如,確定在使用者單擊按鈕時使用正确的參數調用特定功能)。 我們将從測試我們的React元件呈現正确的資料開始,然後着眼于測試互動。 然後,我們繼續快照,這是Jest的一項功能,它使測試React元件的輸出更加友善。

為此,我們需要使用

react-addons-test-utils

,這是一個提供用于測試React的函數的庫。 我們還将安裝Enzyme ,這是由AirBnB編寫的包裝器庫,使測試React元件更加容易。 在整個測試中,我們将使用此API。 Enzyme是一個很棒的庫,React團隊甚至推薦它為測試React元件的方法。

npm install --save-dev react-addons-test-utils enzyme
           

讓我們測試一下

Todo

元件在一個段落中呈現其todo的文本。 首先,我們将建立

__tests__/todo.test.js

,并導入我們的元件:

import Todo from '../app/todo';
import React from 'react';
import { mount } from 'enzyme';

test('Todo component renders the text of the todo', () => {
});
           

我也從酶導入

mount

mount

函數用于呈現我們的元件,然後允許我們檢查輸出并對其進行斷言。 即使我們在Node中運作測試,我們仍然可以編寫需要DOM的測試。 這是因為Jest配置了jsdom ,這是一個在Node中實作DOM的庫。 這很棒,因為我們可以編寫基于DOM的測試,而不必每次都啟動浏覽器進行測試。

我們可以使用

mount

建立我們的

Todo

const todo = { id: 1, done: false, name: 'Buy Milk' };
const wrapper = mount(
  <Todo todo={todo} />
);
           

然後我們可以調用

wrapper.find

,為它提供一個CSS選擇器,以查找我們期望包含Todo文本的段落。 該API可能會讓您想起jQuery,這是設計使然。 這是一個非常直覺的API,用于搜尋渲染的輸出以找到比對的元素。

const p = wrapper.find('.toggle-todo');
           

最後,我們可以斷言其中的文本是

Buy Milk

expect(p.text()).toBe('Buy Milk');
           

整個測試看起來像這樣:

import Todo from '../app/todo';
import React from 'react';
import { mount } from 'enzyme';

test('TodoComponent renders the text inside it', () => {
  const todo = { id: 1, done: false, name: 'Buy Milk' };
  const wrapper = mount(
    <Todo todo={todo} />
  );
  const p = wrapper.find('.toggle-todo');
  expect(p.text()).toBe('Buy Milk');
});
           

! 您可能會認為檢查“購買牛奶”是否已放置在螢幕上需要大量的工作和精力,而且……您是正确的。 不過,現在要抱馬。 在下一節中,我們将研究使用Jest的快照功能來簡化此過程。

同時,讓我們看一下如何使用Jest的間諜功能來斷言使用特定參數調用函數。 這對于我們的情況很有用,因為我們有

Todo

元件,該元件具有兩個作為屬性的函數,當使用者單擊按鈕或執行互動時應調用該函數。

在此測試中,我們将斷言當單擊todo時,元件将調用給定的

doneChange

test('Todo calls doneChange when todo is clicked', () => {
});
           

我們要做的是擁有一個可以跟蹤其調用以及調用其參數的函數。 然後,我們可以檢查使用者單擊待辦事項時是否調用了

doneChange

函數,并使用正确的參數調用了該函數。 值得慶幸的是,Jest開箱即用地提供了間諜程式。 間諜是您不關心其實作的功能。 您隻需關心何時以及如何調用它。 當您監視功能時,請考慮一下它。 要建立一個,我們調用

jest.fn()

const doneChange = jest.fn();
           

這提供了我們可以監視并確定正确調用的功能。 讓我們從使用正确的道具渲染

Todo

開始:

const todo = { id: 1, done: false, name: 'Buy Milk' };
const doneChange = jest.fn();
const wrapper = mount(
  <Todo todo={todo} doneChange={doneChange} />
);
           

接下來,我們可以再次找到我們的段落,就像之前的測試一樣:

const p = TestUtils.findRenderedDOMComponentWithClass(rendered, 'toggle-todo');
           

然後我們可以在其上調用

simulate

來模拟使用者事件,将

click

作為參數傳遞:

p.simulate('click');
           

剩下要做的就是斷言我們的間諜函數已經正确調用。 在這種情況下,我們期望使用todo的ID(即

1

來調用它。 我們可以使用

expect(doneChange).toBeCalledWith(1)

斷言這一點,并以此完成測試!

test('TodoComponent calls doneChange when todo is clicked', () => {
  const todo = { id: 1, done: false, name: 'Buy Milk' };
  const doneChange = jest.fn();
  const wrapper = mount(
    <Todo todo={todo} doneChange={doneChange} />
  );

  const p = wrapper.find('.toggle-todo');
  p.simulate('click');
  expect(doneChange).toBeCalledWith(1);
});
           

使用快照更好地進行元件測試

上面我提到過,測試React元件,尤其是一些更普通的功能(例如渲染文本),可能需要大量工作。 Jest允許您運作快照測試,而不是對React元件進行大量聲明。 這些對于互動并不是很有用(在這種情況下,我仍然喜歡上面我們剛剛寫過的測試),但是對于測試元件輸出是否正确,它們要容易得多。

當您運作快照測試時,Jest會渲染被測的React元件并将結果存儲在JSON檔案中。 每次運作測試時,Jest都會檢查React元件是否仍呈現與快照相同的輸出。 然後,當您更改元件的行為時,Jest會告訴您,并且:

  • 您會發現自己犯了一個錯誤,可以修複該元件,使其再次與快照比對
  • 或者,您有意進行了更改,則可以告訴Jest更新快照。

這種測試方式意味着:

  • 您不必編寫很多斷言即可確定您的React元件的行為符合預期
  • 您永遠都不會意外更改元件的行為,因為Jest會意識到。

您也不必快照所有元件。 實際上,我會積極建議您反對。 您應該選擇具有某些功能的元件,這些功能确實需要確定能正常工作。 對所有元件進行快照會導緻緩慢的測試,這些測試沒有用。 請記住,React是一個經過全面測試的架構,是以我們可以确信它會按預期運作。 確定您最終不要測試架構,而不是代碼!

要開始進行快照測試,我們還需要一個Node軟體包。 react-test-renderer是一個能夠接收React元件并将其呈現為純JavaScript對象的程式包。 這意味着可以将其儲存到檔案中,這就是Jest用來跟蹤快照的方式。

npm install --save-dev react-test-renderer
           

現在,讓我們重寫我們的第一個Todo元件測試以使用快照。 現在,

TodoComponent calls doneChange when todo is clicked

測試一下,注釋掉

TodoComponent calls doneChange when todo is clicked

您需要做的第一件事是導入

react-test-renderer

,并删除

mount

的導入。 它們不能同時使用。 您要麼必須使用另一個。 這就是為什麼我們暫時将另一項測試注釋掉的原因。

import renderer from 'react-test-renderer';
           

現在,我将使用剛剛導入的渲染器來渲染元件,并斷言它與快照比對:

describe('Todo component renders the todo correctly', () => {
  it('renders correctly', () => {
    const todo = { id: 1, done: false, name: 'Buy Milk' };
    const rendered = renderer.create(
      <Todo todo={todo} />
    );
    expect(rendered.toJSON()).toMatchSnapshot();
  });
});
           

第一次運作此指令時,Jest很聰明,意識到該元件沒有快照,是以建立了快照。 讓我們看一下

__tests__/__snapshots__/todo.test.js.snap

exports[`Todo component renders the todo correctly renders correctly 1`] = `
<div
  className="todo  todo-1">
  <p
    className="toggle-todo"
    onClick={[Function]}>
    Buy Milk
  </p>
  <a
    className="delete-todo"
    href="#" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" 
    onClick={[Function]}>
    Delete
  </a>
</div>
`;
           

您可以看到Jest為我們儲存了輸出,現在,下次我們運作此測試時,它将檢查輸出是否相同。 為了示範這一點,我将通過删除呈現待辦事項文本的段落來破壞該元件,這意味着我已經從

Todo

元件中删除了以下行:

<p className="toggle-todo" onClick={() => this.toggleDone() }>{ todo.name }</p>
           

讓我們看看傑斯特現在在說什麼:

FAIL  __tests__/todo.test.js
 ● Todo component renders the todo correctly › renders correctly

   expect(value).toMatchSnapshot()

   Received value does not match stored snapshot 1.

   - Snapshot
   + Received

     <div
       className="todo  todo-1">
   -   <p
   -     className="toggle-todo"
   -     onClick={[Function]}>
   -     Buy Milk
   -   </p>
       <a
         className="delete-todo"
         href="#" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" 
         onClick={[Function]}>
         Delete
       </a>
     </div>

     at Object.<anonymous> (__tests__/todo.test.js:21:31)
     at process._tickCallback (internal/process/next_tick.js:103:7)
           

Jest意識到快照與新元件不比對,并在輸出中告知我們。 如果我們認為此更改是正确的,則可以使用

-u

标志運作jest,它将更新快照。 不過,在這種情況下,我将撤消更改,而Jest再次感到高興。

接下來,我們看一下如何使用快照測試來測試互動。 每個測試可以有多個快照,是以可以測試互動後的輸出是否符合預期。

我們實際上無法通過Jest快照測試Todo元件的互動,因為它們不控制自己的狀态,而是調用給定的回調道具。 我在這裡所做的是将快照測試移至新檔案todo.snapshot.test.js中 ,并将切換測試保留在todo.test.js中 。 我發現将快照測試分成另一個檔案很有用; 這也意味着您不會在

react-test-renderer

react-addons-test-utils

之間

react-test-renderer

沖突。

記住,您會在GitHub上找到我在本教程中編寫的所有代碼,以供您檢出并在本地運作。

結論

Facebook很久以前就釋出了Jest,但最近它被大量使用并投入使用。 它Swift成為JavaScript開發人員的最愛,而且隻會越來越好。 如果您過去曾經嘗試過Jest并且不喜歡它,那麼我不能鼓勵您再嘗試一次,因為現在它實際上是一個不同的架構。 它運作Swift,擅長重新運作規範,可提供出色的錯誤消息,并通過其快照功能使其一臂之力。

如果您有任何疑問,請随時在GitHub上提出問題,我們将竭誠為您服務。 并且請確定在GitHub上檢視Jest并為項目加注星标; 它對維護者有幫助。

本文由Dan Prince和Christoph Pojer進行同行評審。 感謝所有SitePoint的同行評審員使SitePoint内容達到最佳狀态!

翻譯自: https://www.sitepoint.com/test-react-components-jest/

jest測試react元件