React Native的單元測試Jest+Enzyme+storybook
配置
Jest配置
1. 安裝
Jest在React Native利用react-native init AwesomeProject
創(chuàng)建項目的時候就會默認(rèn)安裝了凛俱,這里就不多介紹了院促。
在你使用
create-react-app
或react-native init
創(chuàng)建你的 React 或 React Native 項目時懂版,Jest 都已經(jīng)被配置好并可以使用了米碰。在__tests__
文件夾下放置你的測試用例届垫,或者使用.spec.js
或.test.js
后綴給它們命名。不管你選哪一種方式恋拷,Jest 都能找到并且運行它們响巢。
Enzyme配置
1. 安裝
yarn add enzyme enzyme-adapter-react-16 --dev
每個適配器可能還有其他的對等體依賴關(guān)系,您也需要安裝它們重抖。舉例來說露氮,
enzyme-adapter-react-16
對應(yīng)用同版本的依賴react@16
,react-dom@16
和react-test-renderer@16
钟沛。
2. 初始化配置
由于React Native有很多環(huán)境依賴性畔规,如果沒有主機設(shè)備,很難模擬恨统。所以還需要添加react-native-mock叁扫,如下
yarn add react-native-mock --dev
3. 初始化配置
做后需要配置enzyme的適配器,一個一般要根據(jù)react的版本配置畜埋,現(xiàn)在項目中用的是react@16莫绣,所以如下配置
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import 'react-native-mock/mock';
Enzyme.configure({ adapter: new Adapter() });
還需要讓此配置,在所以test之前執(zhí)行悠鞍,進行如下設(shè)置
// package.json
// ...
"jest": {
// ...
"setupFiles": [
"<rootDir>/__tests__/Setup"
]
}
// ...
Storybook配置
1. 安裝
執(zhí)行下面三條指令就能完成安裝
cd my-project-directory
npm i -g @storybook/cli
getstorybook
2. 運行
npm run storybook
運行
Jest 運行
-
運行全部測試用例
npm jest
-
運行單個測試用例对室,可以借助webstorm工具,來運行咖祭,非常方便
Storybook運行
-
在開發(fā)組件的時候要把storybook運行起來掩宜,并寫stories
npm run storybook
用例
Jest 常用api用法實例
中文 | 英文 |
---|---|
匹配器 | Matchers |
測試異步代碼 | Asynchronous |
模擬器 | Mock Functions |
全局函數(shù) | Global Functions |
API集合
全局方法
afterAll(fn, timeout)
afterEach(fn, timeout)
beforeAll(fn, timeout)
beforeEach(fn, timeout)
describe(name, fn)
describe.each(table)(name, fn)
describe.only(name, fn)
describe.only.each(table)(name, fn)
describe.skip(name, fn)
describe.skip.each(table)(name, fn)
require.requireActual(moduleName)
require.requireMock(moduleName)
test(name, fn, timeout)
test.each(table)(name, fn)
test.only(name, fn, timeout)
test.only.each(table)(name, fn)
test.skip(name, fn)
test.skip.each(table)(name, fn)
匹配器
expect(value)
expect.extend(matchers)
expect.anything()
expect.any(constructor)
expect.arrayContaining(array)
expect.assertions(number)
expect.hasAssertions()
expect.not.arrayContaining(array)
expect.not.objectContaining(object)
expect.not.stringContaining(string)
expect.not.stringMatching(string | regexp)
expect.objectContaining(object)
expect.stringContaining(string)
expect.stringMatching(string | regexp)
expect.addSnapshotSerializer(serializer)
.not
.resolves
.rejects
.toBe(value)
.toHaveBeenCalled()
.toHaveBeenCalledTimes(number)
.toHaveBeenCalledWith(arg1, arg2, ...)
.toHaveBeenLastCalledWith(arg1, arg2, ...)
.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)
.toHaveReturned()
.toHaveReturnedTimes(number)
.toHaveReturnedWith(value)
.toHaveLastReturnedWith(value)
.toHaveNthReturnedWith(nthCall, value)
.toBeCloseTo(number, numDigits)
.toBeDefined()
.toBeFalsy()
.toBeGreaterThan(number)
.toBeGreaterThanOrEqual(number)
.toBeLessThan(number)
.toBeLessThanOrEqual(number)
.toBeInstanceOf(Class)
.toBeNull()
.toBeTruthy()
.toBeUndefined()
.toContain(item)
.toContainEqual(item)
.toEqual(value)
.toHaveLength(number)
.toMatch(regexpOrString)
.toMatchObject(object)
.toHaveProperty(keyPath, value)
.toMatchSnapshot(propertyMatchers, snapshotName)
.toStrictEqual(value)
.toThrow(error)
.toThrowErrorMatchingSnapshot()
Enzyme 常用api用法實例
enzyme有3種渲染方式:render
、mount
么翰、shallow
牺汤,先了解下區(qū)別。
render
硬鞍、mount
慧瘤、shallow
的區(qū)別
render采用的是第三方庫Cheerio
的渲染,渲染結(jié)果是普通的html結(jié)構(gòu)固该,對于snapshot使用render
比較合適。
shallow和mount對組件的渲染結(jié)果不是html的dom樹糖儡,而是react樹伐坏,如果你chrome裝了react devtool插件,他的渲染結(jié)果就是react devtool tab下查看的組件結(jié)構(gòu)握联,而render
函數(shù)的結(jié)果是element tab下查看的結(jié)果桦沉。
這些只是渲染結(jié)果上的差別每瞒,更大的差別是shallow和mount的結(jié)果是個被封裝的ReactWrapper
,可以進行多種操作纯露,譬如find()剿骨、parents()、children()
等選擇器進行元素查找埠褪;state()浓利、props()
進行數(shù)據(jù)查找,setState()钞速、setprops()
操作數(shù)據(jù)贷掖;simulate()
模擬事件觸發(fā)。
shallow只渲染當(dāng)前組件渴语,只能能對當(dāng)前組件做斷言苹威;mount會渲染當(dāng)前組件以及所有子組件,對所有子組件也可以做上述操作驾凶。一般交互測試都會關(guān)心到子組件牙甫,我使用的都是mount
。但是mount耗時更長调违,內(nèi)存啥的也都占用的更多腹暖,如果沒必要操作和斷言子組件,可以使用shallow翰萨。
交互測試
主要利用simulate()
接口模擬事件脏答,實際上simulate是通過觸發(fā)事件綁定函數(shù),來模擬事件的觸發(fā)亩鬼。觸發(fā)事件后殖告,去判斷props上特定函數(shù)是否被調(diào)用,傳參是否正確雳锋;組件狀態(tài)是否發(fā)生預(yù)料之中的修改黄绩;某個dom節(jié)點是否存在是否符合期望。
官方api
[
.context([key\]) => Any
](http://airbnb.io/enzyme/docs/api/ShallowWrapper/context.html
組件測試
-
用storybook做組件測試玷过,既可以存儲組件快照爽丹,也可以快速查看組件樣式
例如:
// import React from 'react'; import { storiesOf } from '@storybook/react-native'; import { action } from '@storybook/addon-actions'; import { linkTo } from '@storybook/addon-links'; import * as img from './img'; import ImageButton from './ImageButton'; storiesOf('<ImageButton />', module) .add('normal', () => <ImageButton title={'確認(rèn)'} imageName={img.ICON_DENE} onPress={action('點擊')} /> ) .add('cancel', () => <ImageButton title={'取消'} imageName={img.ICON_CANCEL} onPress={action('點擊')} /> ) ;
-
效果圖如下
-
根據(jù)組件的需要,進行一些函數(shù)的測試辛蚊,如下
// imageButton.test.js import 'react-native' import React from 'react'; import { mount, shallow } from 'enzyme'; import ImageButton from '../../src/components/ImageButton'; test('<ImageButton/>', () => { let i = 0 const onPress = () => I++ const wrapper = shallow(<ImageButton title={'1'} imageName={null} onPress={onPress}/>); expect(wrapper.instance().props.title).toBe('1'); // uses the right handler expect(wrapper.prop('onPress')).toBe(onPress) expect(i).toBe(0); wrapper.simulate('press'); expect(i).toBe(1); });
API測試
-
API測試主要進行粤蝎,返回狀態(tài)碼(200、500袋马、502等)的驗證初澎,必要字段的返回,指定參數(shù)傳入指定數(shù)據(jù)返回等驗證虑凛,API測試可以在和后臺定義接口的時候就寫碑宴,等后臺接口寫好后直接跑一下測試用例就可驗證软啼。
describe('api', () => { // ... test('/api/config', () => { expect.assertions(1); // 異步斷言數(shù)量 const param = Object.assign(defaultParam, {}); const path = '/api/config'; return api.post(path, param) .then(response => response.data) .then(response => { expect(response.errorCode).toBe(200); }) }); // .... });