react-native使用jest、enzyme進行單元測試

在網上找了很久針對react-native的測試方法,可是沒有找到靠譜的方式。要麼很淺只是跑了一下官方的例子,要麼就是版本有點老舊,照着沒法進行。jest提供的react-native例子不多,而enzyme提供的react-native-mock庫也是各類報錯,讓人非常絕望。因而乎在搜索到的信息指引下,通過本身的嘗試,我總結出了下面的測試方法,可是不是很滿意,若是哪位大神有更好的方式,懇請留個言指明一下方向。javascript

react官方內置了jest做爲單元測試的工具,再搭配上enzyme就能完美地開展測試工做。可是當測試的對象變成react-native時,測試工做就變得異常艱難。艱難的地方主要有如下幾點:java

一、原生組件干擾。
二、使用enzyme去渲染react-native組件時,async/await解析存在問題,我預計是報錯了,而且錯誤被吞吃掉了,這就致使react-native組件的渲染結果不值得信任,由於當你在生命週期函數上使用了async等時,基本能夠確認它的執行存在問題。
三、組件的文件調用了其餘的方法文件時,而且這些方法是異步操做,包含了數據請求操做等,如何去mock數據而且替換調用。
四、Image組件使用require獲取文件報錯。
基於以上幾點問題,咱們開始一一解決。
一、官方提供了配置能夠模擬原生組件,當有第三方組件時,能夠經過jest.mock方法作模擬。
假定引入了一個第三方組件叫作react-native-go,而且上面有一個方法叫to,而後你在代碼中調用了這個方法,那麼你就能夠這樣寫一個假的對象來返回對應方法,具體實現以下
jest.mock('react-native-go', ()=>{
    return {
        to: ()=>{}
    }
});
二、既然內部調用async有問題,那麼咱們可不能夠直接在外部調用內部方法呢?固然你直接用shallow返回的組件對象去調用內部方法是不行的,這時候只會報錯,說你調用的對象不是函數。那麼怎麼獲得this指針呢?組件在初始化時會執行各個生命週期函數,而咱們能夠經過props傳遞函數給組件來調用,這就有解決沒法獲取this的問題的途徑了。接着咱們就能夠經過閉包函數和修改函數指針的方式來把this暴露到測試環境中。
三、組件內部引用了外部文件。這個也簡單了,基於第二點的辦法,咱們就能夠造一個測試專用的方法對象來替換掉實際使用的工具對象,經過props和生命週期函數直接覆蓋掉,這樣再加上mockjs即可以模擬數據。
四、 參照官方文檔中Image的另外一個調用方式是uri,而且能夠直接傳入base64文件,那麼咱們就能夠把require去掉,改爲直接引用base64文件。而圖片轉換base64,咱們能夠經過nodejs來實現。
經過上面這些方式,咱們就能夠開始react-native的測試工做了。
謝謝 walkOnly提供的解決方法。
圖片引入的正確解決方式是經過package.json中jest的配置來解決的。可使用transform來將其替換成assetFileTransformer.js。
這是assetFileTransformer.js的源碼
'use strict';

/* eslint-env node */

const path = require('path');
const createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction');

module.exports = {
  // Mocks asset requires to return the filename. Makes it possible to test that
  // the correct images are loaded for components. Essentially
  // require('img1.png') becomes `Object { "testUri": 'path/to/img1.png' }` in
  // the Jest snapshot.
  process: (_, filename) =>
    `module.exports = {
      testUri: ${JSON.stringify(path.relative(__dirname, filename))}
    };`,
  getCacheKey: createCacheKeyFunction([__filename]),
};
 
 
具體代碼以下:
首先,按照配置jest測試環境。
一、安裝依賴包
"devDependencies": {
	"enzyme": "^3.3.0",
	"enzyme-adapter-react-16": "^1.1.1",
	"jest": "^21.2.1",
	"react-dom": "^16.2.0"
},

二、編寫.babelrcnode

{
  "presets": ["react-native"]
}

三、jest配置react

"scripts": {
    "test": "jest"
  },
"jest": {
    "preset": "react-native",
    "transform": {
      "\\.(jpg|jpeg|png|webp|gif|svg|wav|mp3|mp4|m4a|aac)$": "<rootDir>/node_modules/react-native/jest/assetFileTransformer.js"
    }
  }

 

先寫一個我要測試的組件web

import React, {Component} from 'react';
import {
  View
} from 'react-native';

//工具方法包含獲取數據請求send
let Core = require('./core');

export default class AboutUsPage extends Component<{}>{
  constructor(props){
    super(props);
    if(typeof this.props.getThis === 'function'){
      this.props.getThis.call(this);
      if (this.props.testCore) {
        Core = this.props.testCore;
      }
    }
  }
  async componentWillMount(){
    this.setState({
      name: await this.firstStep()
    })
  }
  async firstStep(){
    return await this.secondStep();
  }
  async secondStep(){
    return await Core.send('/getData');
  }
  render(){
    return (
      <View></View>
    )
  }
}

core文件json

let Core = {
    async send() {//請求異步數據,返回promise
        ...
    }
};
module.exports = Core;

testCore文件,暴露兩個函數,一個send用以調用數據,一個setSendData用以設置要返回的數據react-native

"use strict";

let caches = {
};

let currentRequest = {};
let Core = {
  async setSendData(key, status, data) {
    caches[key] = {
      status,
      data
    };
  },
  async send(key){
    let res = caches[key];
    if(res.status){
      return Promise.resolve(res.data);
    }else{
      return Promise.reject(res.data);
    }
  }
};

module.exports = Core;

test.js測試文件promise

'use strict';
import React from 'react';
import AboutUsPage from './AboutUsPage';
import {configure, shallow} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import testCore from './test.core';

configure({ adapter: new Adapter() });

testCore.setSendData('getData', true, 'aaa');

describe('AboutUsPage', () => {
  let component;
  let wrapper;
  wrapper = shallow(<AboutUsPage getThis={function(){component=this;}} testCore={testCore}/>);
  wrapper.setState({
    name: 'tom'
  });
  it('renders correctly', async () => {
    await component.componentWillMount();
    expect(wrapper.state().name).toBe('aaa');
  });
  
});
相關文章
相關標籤/搜索