【譯】如何淺渲染(shallow render) Jest 快照測試

如果你的組件正在使用 Jest 快照測試荞驴,下面是一些你必須注意的陷阱。這兩條你可能會遇到在你編寫測試的時候:

  1. 如果實際的快照測試渲染了一個包含很多子組件的組件脓魏,那么輸出的快照測試將變得太大溶耘。這個本身就存在兩個問題:A) 僅僅通過查看快照并不能準確的發(fā)現(xiàn)快照不同的地方;B) 如果你同時快照測試你的子組件鼻忠,最終會得到重復的快照輸出涵但。
  2. 如果你實際的快照測試組件渲染了大量子組件,則需要在父組件的快照測試中設(shè)置子組件的所有 props帖蔓。因此矮瘟,你并沒有真正將關(guān)注點放在父組件上,而是設(shè)置子組件所有必要的信息塑娇。如果你再分開測試你的子組件澈侠,工作將變得重復,因為你必須使用相同的 props 設(shè)置來測試他們埋酬。最終你會得到重復的測試設(shè)置哨啃。

如你所見,這兩個問題只適用于渲染多個子組件的父組件奇瘦。因此,如果你可以在快照測試中淺層渲染(shadow render)父組件劲弦,從而在測試中只關(guān)注父組件耳标;以及不管有沒有渲染了子組件都不用擔心子組件的輸出。

如果你正在使用 Jest 快照測試邑跪,你可能使用 react-test-renderer 渲染你的 React 組件:

import React from 'react';
import renderer form 'react-test-renderer';

import Profile from '.';

describe('Profile', () => {
  it('renders', () => {
    const component = renderer.create(<Profile />);
    const tree = component.toJSON();
    expect(tree).toMatchSnapShot();
  });
});

如果在 Profile 組件中呈現(xiàn)許多子組件次坡,那么快照測試輸出可能會出現(xiàn)問題1呼猪。不過,您唯一應(yīng)該關(guān)心的是呈現(xiàn)的組件實例砸琅,而不是它們的內(nèi)容:

const Profile = () => (
  <>
    <Preferences />
    <Documents />
    <WorkExperience />
    <Education />
    <Skills />
    <PersonalInfo />
  </>
);

如果 Profile 組件傳遞大量的 props 給所有的子組件宋距,那么就會出現(xiàn)第二個問題,因為你必須為所有的子組件設(shè)置 props 在你快照測試症脂,即使父組件可能并不關(guān)心它們:

const Profile = ({
  ...preferencesProps,
  ...documentsProps,
  ...workExperienceProps,
  ...educationProps,
  ...skillsProps,
  ...personalInfoProps,
}) => (
  <>
    <Preferences {...preferencesProps} />
    <Documents {...documentsProps} />
    <WorkExperience {...workExperienceProps} />
    <Education {...educationProps} />
    <Skills {...skillsProps} />
    <PersonalInfo {...personalInfoProps} />
  </>
);

你希望避免上面兩個問題在父組件快照測試的時候谚赎,因為這些問題應(yīng)該在子組件他們自己進行測試。父組件只關(guān)心渲染這些子組件诱篷。

筆記:淺層渲染(Shallow rendering)快照測試對于你整體的測試戰(zhàn)略來說并不是靈丹妙藥壶唤,你可能會對你的組件工作在集成環(huán)境失去信心(例如:父子組件交互)

盡管 React 的測試渲染器提供了 shallow rendering ,但我發(fā)現(xiàn)虛擬子組件的渲染輸出更適合我的測試用例:

import React from 'react';
import renderer from 'react-test-renderer';

import Profile from '.';

jest.mock('./Preferences', () => () => 'Preferences');
jest.mock('./Documents', () => () => 'Documents');
jest.mock('./WorkExperience', () => () => 'WorkExperience');
jest.mock('./Education', () => () => 'Education');
jest.mock('./Skills', () => () => 'Skills');
jest.mock('./PersonalInfo', () => () => 'PersonalInfo');

describe('Profile', () => {
  it('renders', () => {
    const component = renderer.create(<Profile />);
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });
});

你的淺層渲染快照測試輸出可能看起來像下面這樣:

exports[`Profile renders 1`] = `
Array [
  "Preferences",
  "Documents",
  "WorkExperience",
  "Education",
  "Skills",
  "PersonalInfo",
]
`;

與渲染所有子組件來做版本比較相比棕所,它做到了最大程度的簡化闸盔。此外,你也不需要關(guān)心 props 的傳遞琳省。然而迎吵,如果你想測試你的父組件是否傳遞了必須的 props 給它的子組件,你甚至可以用一個虛擬的子組件來測試它:

import React from 'react';
import renderer from 'react-test-renderer';

import Profile from '.';
import PersonalInfo from './PersonalInfo';

jest.mock('./Preferences', () => () => 'Preferences');
jest.mock('./Documents', () => () => 'Documents');
jest.mock('./WorkExperience', () => () => 'WorkExperience');
jest.mock('./Education', () => () => 'Education');
jest.mock('./Skills', () => () => 'Skills');
jest.mock('./PersonalInfo', () => () => 'PersonalInfo');

describe('Profile', () => {
  it('renders and passes props', () => {
    const component = renderer.create(<Profile />);
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();

    expect(component.root.findByType(PersonalInfo).props).toEqual({
      name: 'Robin Wieruch',
    });
  });
});

總而言之针贬,你最終為父組件創(chuàng)建了一個非常輕量級的快照測試击费,而你將在子組件自身更徹底地進行快照測試。


React 官方文檔

測試渲染器 Test Renderer(https://reactjs.org/docs/test-renderer.html

導入

import TestRenderer from 'react-test-renderer'; // ES6
const TestRenderer = require('react-test-renderer'); // ES5 with npm

總覽

這個包提供了一個 React 渲染器(React renderer)可以將 React 組件渲染為純的 JavaScript 對象坚踩,而不用依賴于 DOM 或本地移動環(huán)境荡灾。

本質(zhì)上,這個包可以使你輕松抓取一個由 React DOM 或 React Native 組件渲染的平臺視圖層級(類似于 DOM 樹)快照瞬铸,而無需使用瀏覽器或 jsdom批幌。

例子:

import TestRenderer from 'react-test-renderer';

function Link(props) {
  return <a href={props.page}>{props.children}</a>;
}

const testRenderer = TestRenderer.create(
  <Link page="https://www.fackbook.com/">
);

console.log(testRenderer.toJSON());
// { type: 'a',
//   props: { href: 'https://www.facebook.com/' },
//   children: [ 'Facebook' ] }

你可以使用 Jest 的快照測試特性自動化地保存一份 JSON 樹的復制到一個文件中,并檢查你的測試沒有發(fā)生變化:這里學習到更多嗓节。

你也可以遍歷輸出來找到特殊的節(jié)點荧缘,并對它們進行斷言。

import TestRenderer from 'react-test-renderer';

function MyComponent() {
  return (
    <div>
      <SubComponent foo="bar" />
      <p className="my">Hello</p>
    </div>
  )
}

function SubComponent() {
  return (
    <p className="sub">Sub</p>
  );
}

const testRenderer = TestRenderer.create(<MyComponent />);
const testInstance = testRenderer.root;

expect(testInstance.findByType(SubComponent).props.foo).toBe('bar');
expect(testInstance.findByProps({className: "sub"}).children).toEqual(['Sub']);

參考
TestRenderer.create()
TestRenderer.create(element, options);

使用傳遞的 React 元素創(chuàng)建一個 TestRenderer 實例拦宣。它不使用真正的DOM截粗,但仍然將組件樹完全呈現(xiàn)到內(nèi)存中,因此您可以對其進行斷言鸵隧。返回一個 TestRenderer 實例绸罗。

TestRenderer.act()
TestRenderer.act(callback);

類似于 react-dom/test-utils 里面的 act() 方法,TestRenderer.act 為斷言準備一個組件豆瘫。使用這個版本的 act() 就是封裝調(diào)用 TestRenderer.create and TestRenderer.update珊蟀。

import {create, act} from 'react-test-renderer';
import App from './app.js'; // The component being tested

// render the component
let root; 
act(() => {
  root = create(<App value={1}/>)
});

// make assertions on root 
expect(root.toJSON()).toMatchSnapshot();

// update with some different props
act(() => {
  root = root.update(<App value={2}/>);
})

// make assertions on root 
expect(root.toJSON()).toMatchSnapshot();
testRenderer.toJSON()
testRenderer.toJSON();

返回一個對象呈現(xiàn)渲染樹??。這個樹僅僅包含平臺特定的節(jié)點外驱,如 <div> 或 <View> 和他們的 props育灸,但是不包含用戶編寫的組件腻窒。這對于快照測試十分方便。

testRenderer.toTree()

和 toJson() 方法唯一的區(qū)別就是它包含用戶編寫的組件磅崭。

......


React 文檔

Shallow Renderer

導入

import ShallowRenderer from 'react-test-renderer/shallow'; // ES6
var ShallowRenderer = require('react-test-renderer/shallow'); // ES5 with npm
總覽

在編寫React的單元測試時儿子,淺層呈現(xiàn)可能會有幫助。淺層呈現(xiàn)允許您呈現(xiàn)一個組件“一層深”砸喻,并斷言關(guān)于其呈現(xiàn)方法返回什么的事實柔逼,而不用擔心子組件的行為,這些子組件沒有實例化或呈現(xiàn)恩够。這并不需要DOM卒落。

例如,如果你擁有如下組件:

function MyComponent() {
  return (
    <div>
      <span className="heading">Title</span>
      <Subcomponent foo="bar" />
    </div>
  );
}

然后你可以斷言:

import ShallowRenderer from 'react-test-renderer/shallow';

// in your test:
const renderer = new ShallowRenderer();
renderer.render(<MyComponent />);
const result = renderer.getRenderOutput();

expect(result.type).toBe('div');
expect(result.props.children).toEqual([
  <span className="heading">Title</span>,
  <Subcomponent foo="bar" />

shallow 測試當前有一些限制蜂桶,例如不支持 refs儡毕。

提示:我們建議你使用 Enzyme 的 Shallow Rendering API 它在相同的功能上提供了更高級的 API


參考
shallowRenderer.render()

您可以將shallowRenderer視為呈現(xiàn)正在測試的組件的“位置”,并從中提取組件的輸出扑媚。

render()類似于ReactDOM.render()腰湾,但它不需要DOM,只呈現(xiàn)一個層次的深度疆股。這意味著您可以測試與子組件的實現(xiàn)方式隔離的組件费坊。

shallowRenderer.getRenderOutput()

調(diào)用了shallowRenderer.render()之后,您可以使用shallowRenderer.getRenderOutput()獲得淺呈現(xiàn)的輸出旬痹。

然后可以開始斷言關(guān)于輸出的事實附井。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市两残,隨后出現(xiàn)的幾起案子永毅,更是在濱河造成了極大的恐慌,老刑警劉巖人弓,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沼死,死亡現(xiàn)場離奇詭異,居然都是意外死亡崔赌,警方通過查閱死者的電腦和手機意蛀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來健芭,“玉大人县钥,你說我怎么就攤上這事〈嚷酰” “怎么了若贮?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我兜看,道長,這世上最難降的妖魔是什么狭瞎? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任细移,我火速辦了婚禮,結(jié)果婚禮上熊锭,老公的妹妹穿的比我還像新娘弧轧。我一直安慰自己,他們只是感情好碗殷,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布精绎。 她就那樣靜靜地躺著,像睡著了一般锌妻。 火紅的嫁衣襯著肌膚如雪代乃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天仿粹,我揣著相機與錄音搁吓,去河邊找鬼。 笑死吭历,一個胖子當著我的面吹牛堕仔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晌区,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼摩骨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了朗若?” 一聲冷哼從身側(cè)響起恼五,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捡偏,沒想到半個月后唤冈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡银伟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年你虹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彤避。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡傅物,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出琉预,到底是詐尸還是另有隱情董饰,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站卒暂,受9級特大地震影響啄栓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜也祠,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一昙楚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诈嘿,春花似錦堪旧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至昔字,卻和暖如春爆袍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背作郭。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工螃宙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人所坯。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓谆扎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芹助。 傳聞我的和親對象是個殘疾皇子堂湖,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355