我一直在努力让Jest在没有太多运气的情况下参与我的react-native 项目.似乎大多数线程都是黑客破解的解决方案,用于启动和运行,而我似乎无法克服我面临的最后一个障碍.

问题

在try 运行下面的代码时,我遇到了以下错误.如果我在jestSupport/env中模仿react.js文件我可以克服这个错误,但显然我不能使用AsyncStorage之类的任何 struct 来实际测试我的代码(因为我只有模拟的功能).

问题

有人对如何解决这个问题有什么建议吗?

在这个阶段,我愿意try 任何事情,包括放弃所有与测试相关的东西,然后再试一次.如果是这样的话,我需要遵循一些指导原则,因为React原生文档关于jest已经过时了,而且我对React场景还比较陌生.

错误

Runtime 错误
错误: Cannot find module 'ReactNative' from 'react-native.js'
    at Runtime._resolveNodeModule (/Users/Yulfy/Downloads/COMPANY-Mobile/node_modules/jest-cli/src/Runtime/Runtime.js:451:11)
    at Object.<anonymous> (/Users/Yulfy/Downloads/COMPANY-Mobile/node_modules/react-native/Libraries/react-native/react-native.js:181:25)
    at Object.<anonymous> (/Users/Yulfy/Downloads/COMPANY-Mobile/network/connections.js:8:18)

测试代码

jest.unmock('../network/connections');

import Authorisation from '../network/connections';

describe('connections', () => {
  it('Should store and retrieve a mocked user object', () => {
    Auth = new Authorisation();
    const TEST_STRING = "CONNECTION TEST PASS";
    var userObj = {
      test_string: TEST_STRING
    };
    Auth._localStore(userObj, (storeRes) => {
      Auth._localRetrieve((retRes) => {
        expect(retRes.test_string).toEqual(TEST_STRING);
      });
    });
  });
});

联系js

/*
*  All returns should give the following structure:
*  {isSuccess: boolean, data: object}
*/


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

const Firebase = require('firebase');
const FIREBASE_URL = 'https://COMPANY-test.firebaseio.com';
const STORAGE_KEY = 'USER_DATA';
class Authorisation{
  _ref = null;
  user = null;

  constructor(){

  }

  getOne(){return 1;}
  _setSystemUser(userObj, authObj, callback){
    var ref = this.connect();
    if(ref === null){
      callback({isSuccess:false, data:{message:"Could not connect to the server"}});
    }
    ref = ref.child('users').child(authObj.uid);
    ref.once("value", function(snapshot){
      if(snapshot.exists()){
        callback({isSuccess:false, data:{message:"Email is currently in use"}});
        return;
      }
      ref.set(userObj, function(error){
        if(error){
          callback({isSuccess:false, data:error});
        }else{
          callback({isSuccess:true, data:authObj});
        }
      });
    });
  }

  _localStore(userObj, callback){
    AsyncStorage.setItem(STORAGE_KEY, userObj, (error) => {
      console.log("_localStore::setItem -> ", error);
      if(error){
        callback({
          isSuccess:false,
          data:'Failed to store user object in storage.'
        });
      }else{
        callback({
          isSuccess:true,
          data: userObj
        });
      }
    });
  }
  _localRetrieve(callback){
    AsyncStorage.getItem(STORAGE_KEY, (error, res) => {
      console.log("_localStore::getItem:error -> ", error);
      console.log("_localStore::getItem:result -> ", res);
      if(error){
        callback({
          isSuccess:false,
          data:error
        });
      }else{
        callback({
          isSuccess: true,
          data: res
        });
      }
    });
  }

  connect(){
    if(this._ref === null){
      _ref = new Firebase(FIREBASE_URL);
    }
    return _ref;
  }

  getUser(){

  }

  isLoggedIn(){

  }

  registerUser(userObj, callback){
    var ref = this.connect();
    if(ref === null){
      callback({isSuccess:false, data:{message:"Could not connect to the server"}});
    }
    var that = this;
    ref.createUser({
      email: userObj.username,
      password: userObj.password
    }, function(error, userData){
      if(error){
        callback({isSuccess:false, data:error});
        return;
      }
      var parseObj = {
        email: userObj.username,
        fullName: userObj.fullName
      };
      that.loginUser(parseObj, function(res){
        if(res.isSuccess){
          that._setSystemUser(parseObj, res.data, callback);
        }else{
          callback(res);//Fail
        }
      });
    });
  }

  loginUser(userObj, callback){
    var ref = this.connect();
    if(ref === null){
      callback({isSuccess:false, data:{message:"Could not connect to the server"}});
    }
    ref.authWithPassword({
      email: userObj.email,
      password: userObj.password
    }, function(error, authData){
      if(error){
        callback({isSuccess:false, data:error.message});
      }else{
        callback({isSuccess:true, data:authData});
      }
    });

  }
}

export default Authorisation;

如果你已经读到这里,谢谢你的时间!

-尤菲

推荐答案

TL;DR

我有一个Jest运行的工作示例,在这GitHub Repo中使用了最新版本React Native(v0.28.0).

-

在调查这个问题一段时间后,我终于找到了解决办法.

有几个与Jest集成的React本机应用程序的在线示例,但不幸的是,您不能简单地将代码复制并粘贴到您的代码库中,并期望它能够工作.这是因为RN版本的差异.

React Native v0.20.0之前的版本在打包机(node_modules/react-native/packager/react-packager/.babelrc)中包含.babelrc文件,一些在线示例直接将其包含在package.json中.但是,版本v0.20.0及更高版本已更改为不再包含此文件,这意味着您无法再try 包含它.因此,我建议您使用自己的.babelrc文件,并定义自己的预设和插件.

我不知道你的package.json文件是什么样子,但它对解决这个问题来说是非常重要的一部分.

{
    "name": "ReactNativeJest",
    "version": "0.0.1",
    "jest": {
        "scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
        "unmockedModulePathPatterns": [
            "node_modules"
        ],
        "verbose": true,
        "collectCoverage": true
    },
    "scripts": {
        "test": "jest"
    },
    "dependencies": {
        "react": "^15.1.0",
        "react-native": "^0.27.2"
    },
    "devDependencies": {
        "babel-core": "^6.4.5",
        "babel-jest": "^12.1.0",
        "babel-plugin-transform-regenerator": "^6.0.18",
        "babel-polyfill": "^6.0.16",
        "babel-preset-react-native": "^1.9.0",
        "babel-types": "^6.1.2",
        "chai": "^3.5.0",
        "enzyme": "^2.3.0",
        "jest-cli": "^12.1.1",
        "react-addons-test-utils": "^15.1.0",
        "react-dom": "^15.1.0"
    }
}

另一个重要的部分是模仿.我创建了一个__mocks__/react-native.js文件,如下所示:

'use strict';

var React = require('react');
var ReactNative = React;

ReactNative.StyleSheet = {
    create: function(styles) {
        return styles;
    }
};

class View extends React.Component {}
class Text extends React.Component {}
class TouchableHighlight extends React.Component {}

// Continue to patch other components as you need them
ReactNative.View = View;
ReactNative.Text = Text;
ReactNative.TouchableHighlight = TouchableHighlight;

module.exports = ReactNative;

通过像这样对React本机函数进行monkey修补,可以成功避免在运行测试时出现奇怪的Jest错误.

最后,确保在项目的根目录中创建一个至少包含以下行的.babelrc文件:

{
    "presets": ["react-native"],
    "plugins": [
        "transform-regenerator"
    ]
}

此文件将用于告诉巴贝尔如何正确转换ES6代码.

完成此设置后,使用React Native运行Jest应该没有问题.我相信React Native的future 版本将使这两个框架更容易集成在一起,但这种技术在当前版本中将非常有效:)

EDIT

不用手动模拟__mocks__/react-native.js文件中的ReactNative元素,您可以使用react-native-mock库为您进行模拟(确保将库添加到package.json文件中):

// __mocks__/react-native.js

module.exports = require('react-native-mock');

我更新了我的GitHub Repo个例子来演示这个方法.

React-native相关问答推荐

出现错误:使用 process(css).then(cb) 来使用异步插件

React Native - 如何在从另一个屏幕导航后将 ScrollView 滚动到给定位置

如何在 React Navigation 5 中将父函数传递给子屏幕?

_react2.default.PropTypes.function 未定义

react-navigation中的React-native android后退按钮

更改所选标签栏元素的底部边框 colored颜色

在 React 中使用 Lodash debounce 来防止在用户输入时请求数据

如何在StackNavigator 中实现自定义标题图标?

设置 rootView.appProperties 不会重新渲染根组件

缺少请求的请求令牌

如何在 React Native 的 Modal 组件上禁用Swipe down to close?

无法解析模块 react/lib/ReactUpdates

使用 WSL2 运行时如何将手机连接到 expo

React Native 中使用了哪些维度单位?

react-native Bottom Up drawer

React Native 动画 - zoom 时 translateX 和 translateY

我可以使用 react native 开发 pwa

INSTALL_FAILED_UPDATE_INCOMPATIBLE:包签名与之前安装的版本不匹配,忽略

React Native:使用百分比时将视图的宽度设置为等于其高度

如何在 React Native 上隐藏 createStackNavigator 的标题?