轉載位址
React 16 Jest快照測試
項目初始化
git clone https://github.com/durban89/webpack4-react16-reactrouter-demo.git
cd webpack4-react16-reactrouter-demo
git fetch origin
git checkout v_1.0.24
npm install
隻要想確定UI不會意外更改,快照測試是非常有用的工具。
移動應用程式的典型呈現UI元件快照測試用例,通過截取螢幕截圖,然後将其與存儲在測試這邊的參考圖像進行比較。
如果兩個圖像不比對,測試将失敗,可能是被意外的更改了,或者需要将螢幕截圖更新為新版本的UI元件。
Jest快照測試
在測試React元件時,可以采用類似的方法。
可以使用測試渲染器快速生成React樹的可序列化值,而不是渲染需要建構整個應用程式的圖形UI。
下面做一個簡單的Link元件的示例測試:
測試之前先安裝下react-test-renderer依賴庫
npm install react-test-renderer --save-dev
src/__tests__/react_link.react.jsx
import React from 'react';
import renderer from 'react-test-renderer';
import ALink from '../components/Link.react';
it('正确的渲染', () => {
const tree = renderer
.create(<ALink page=" https://www.gowhich.com ">Gowhich</ALink>)
.toJSON(); expect(tree).toMatchSnapshot();
});
src/components/Link.react.jsx
import React from 'react';
import PropTypes from 'prop-types';
const STATUS = {
HOVERED: 'hovered',
NORMAL: 'normal',
};
class Link extends React.Component {
constructor(props) {
super(props);
this.onMouseEnter = this.onMouseEnter.bind(this);
this.onMouseleave = this.onMouseleave.bind(this);
this.state = {
class: STATUS.NORMAL,
};
}
onMouseEnter() {
this.setState({
class: STATUS.HOVERED,
});
}
onMouseleave() {
this.setState({
class: STATUS.NORMAL,
});
}
render() {
return (
<a
className={this.state.class}
href={this.props.page || '#'}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseleave}
>
{this.props.children}
</a>
);
}
}
Link.propTypes = {
page: PropTypes.string.isRequired,
children: PropTypes.oneOfType([
PropTypes.element,
PropTypes.string,
]).isRequired,
};
export default Link;
第一次運作此測試時,Jest會建立一個如下所示的快照檔案:
src/__tests__/__snapshots__/react_link.test.jsx.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`正确的渲染 1`] = `
<a
className="normal"
href=" https://www.gowhich.com "
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Gowhich
</a>
`;
快照檔案應該與代碼更改一起送出,并作為代碼審查過程的一部分進行稽核。
Jest使用pretty-format對快照檔案進行了處理,當代碼在審查期間,會讓代碼快照變成讓人類可閱讀的檔案。
在随後的測試運作中,Jest會将渲染的輸出的檔案與之前的快照進行比較。
如果比對,測試将通過。如果它們不比對,表示代碼出問題了。Jest則會告知代碼中哪裡需要進行修改的錯誤或者是代碼的邏輯需要進行更新,重新生成快照。我們是試着更改下page參數的位址和Gowhich文案,如下
import React from 'react';
import renderer from 'react-test-renderer';
import ALink from '../components/Link.react';
it('正确的渲染', () => {
const tree = renderer
.create(<ALink page=" https://www.walkerfree.com ">Walkerfree</ALink>)
.toJSON(); expect(tree).toMatchSnapshot();
});
再次執行測試,會得到類似如下的輸出,這裡我就隻截取錯誤這部分的内容
正确的渲染 (22ms)
● 正确的渲染
expect(value).toMatchSnapshot()
Received value does not match stored snapshot "正确的渲染 1".
- Snapshot
+ Received
<a
className="normal"
- href=" https://www.gowhich.com "
+ href=" https://www.walkerfree.com "
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
- Gowhich
+ Walkerfree
</a>
6 | const tree = renderer
7 | .create(<ALink page=" https://www.walkerfree.com ">Walkerfree</ALink>)
> 8 | .toJSON(); expect(tree).toMatchSnapshot();
| ^
9 | });
10 |
at Object.<anonymous> (src/__tests__/react_link.test.jsx:8:29)
› 1 snapshot failed.
Snapshot Summary
› 1 snapshot failed from 1 test suite. Inspect your code changes or re-run jest with `-u` to update them.
由于剛剛更新了元件以指向不同的位址和文案,而且這次更改是合理的,是我們希望的邏輯,這個時候快照測試會失敗,因為剛更新的元件的快照不再與此測試用例的快照相比對。
為了解決這個問題,我們需要更新快照檔案,隻需要運作下面的指令,就會重新生成快照
npx jest src/__tests__/react_link.test.jsx --notify --watchman=false --updateSnapshot
這個時候我們就需要同之前一樣,将這個新的快照檔案同代碼的更改一起送出。
快照互動模式
執行如下指令
npx jest src/__tests__/react_link.test.jsx --notify --watchman=false --updateSnapshot --watch
隻是加了一個
--watch
運作上面的指令會得到類似如下的輸出
PASS src/__tests__/react_link.test.jsx
正确的渲染 (17ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 passed, 1 total
Time: 1.713s
Ran all test suites matching /src\/__tests__\/react_link.test.jsx/i.
Active Filters: filename /src/__tests__/react_link.test.jsx/
› Press c to clear filters.
Watch Usage
› Press a to run all tests.
› Press f to run only failed tests.
› Press o to only run tests related to changed files.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
› Press q to quit watch mode.
› Press Enter to trigger a test run.
而且會對測試檔案進行檢測。如果有檔案改動了,會自動重新啟動測試,然後對測試檔案,進行重新測試屬性比對器
通常有時候,一個對象的屬性的值被生成,但是在進行快照生成時候,這個值就固定了,下載下傳再次執行測試的時候就會報錯,如下
it('這個快照再次測試的時候會失敗', () => {
const user = {
createAt: new Date(),
id: Math.floor(Math.random() * 20),
name: 'Durban',
};
expect(user).toMatchSnapshot();
});
執行後生成的快照如下
// Jest Snapshot v1, https://g oo.gl/fbAQLP
exports[`這個快照再次測試的時候會失敗 1`] = `
Object {
"createAt": 2018-07-06T06:44:49.429Z,
"id": 0,
"name": "Durban",
}
`;
其實從下面這裡我們就已經能發現問題了
"createAt": 2018-07-06T06:44:49.429Z,
日期是每次執行一次都會變化的。當我們再次執行的時候會發現報出了如下錯誤
這個快照再次測試的時候會失敗 (14ms)
● 這個快照再次測試的時候會失敗
expect(value).toMatchSnapshot()
Received value does not match stored snapshot "這個快照再次測試的時候會失敗 1".
- Snapshot
+ Received
Object {
- "createAt": 2018-07-06T06:44:49.429Z,
- "id": 0,
+ "createAt": 2018-07-06T06:46:02.245Z,
+ "id": 10,
"name": "Durban",
}
6 | };
7 |
> 8 | expect(user).toMatchSnapshot();
| ^
9 | });
10 |
at Object.<anonymous> (src/__tests__/jest_snap_property.test.js:8:16)
› 1 snapshot failed.
Snapshot Summary
› 1 snapshot failed from 1 test suite. Inspect your code changes or re-run jest with `-u` to update them.
針對這個情況我們可以這樣解決,在測試裡面加入如下代碼
expect(user).toMatchSnapshot({
createAt: expect.any(Date),
id: expect.any(Number),
});
修改後代碼如下
it('檢查比對器并測試通過', () => {
const user = {
createAt: new Date(),
id: Math.floor(Math.random() * 20),
name: 'Durban',
};
expect(user).toMatchSnapshot({
createAt: expect.any(Date),
id: expect.any(Number),
});
});
執行更新操作,對快照進行更新得到的快照内容結果如下
// Jest Snapshot v1, https://g oo.gl/fbAQLP
exports[`檢查比對器并測試通過 1`] = `
Object {
"createAt": Any<Date>,
"id": Any<Number>,
"name": "Durban",
}
`;
項目實踐位址
https:// github.com/durban89/webpack4-react16-reactrouter-demo.git
tag:v_1.0.25