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

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

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

如你所見,這兩個問題只適用于渲染多個子組件的父組件。因此,如果你可以在快照測試中淺層渲染(shadow render)父組件,從而在測試中只關(guān)注父組件;以及不管有沒有渲染了子組件都不用擔(dā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

導(dǎo)入

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ù)制到一個文件中,并檢查你的測試沒有發(fā)生變化:這里學(xué)習(xí)到更多。

你也可以遍歷輸出來找到特殊的節(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 為斷言準(zhǔn)備一個組件。使用這個版本的 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

導(dǎo)入

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)方法返回什么的事實,而不用擔(dā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 測試當(dāng)前有一些限制,例如不支持 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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容