简体   繁体   English

使用Jest测试React Native应用程序

[英]Testing React Native apps with Jest

I've been trying to get Jest working with my react native project without much luck. 我一直试图让Jest使用我的反应原生项目而没有太多运气。 Seems most threads are hacked solutions to get things up and running and I can't seem to get over the last hurdle I'm facing. 似乎大多数线程都是被黑客攻击的解决方案,以便让事情正常运行,我似乎无法克服我面临的最后一个障碍。

Problem 问题

I'm getting the following error when trying to run the following piece of code. 尝试运行以下代码时,我收到以下错误。 If I mock react-native inside of the jestSupport/env.js file I can get past the error but obviously I cannot use any of the structures such as AsyncStorage to actually test my code (as I'll only have the mocked functionality). 如果我在jestSupport / env.js文件中模拟react-native我可以通过错误,但显然我不能使用任何结构(如AsyncStorage)来实际测试我的代码(因为我只有模拟功能)。

Question

Does anyone have any suggestions on how to solve this problem? 有没有人对如何解决这个问题有任何建议?

At this stage I'm willing to try anything up to and including scrapping everything test related that I have and trying again. 在这个阶段,我愿意尝试任何事情,包括废弃我所拥有的所有测试,然后再试一次。 If that were the case, I'd need some set of guidelines to follow as the React Native docs are horribly out of date regarding Jest and I'm relatively new to the React scene. 如果是这种情况,我需要遵循一些指导原则,因为React Native文档在Jest方面可能已经过时,而且我对React场景相对较新。

Error 错误

Runtime Error
Error: 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)

Test Code 测试代码

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);
      });
    });
  });
});

connection.js connection.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;

If you've read this far, thanks for your time! 如果您已经读过这篇文章,感谢您的时间!

-Yulfy -Yulfy

TL;DR TL; DR

I have a working example of Jest running with the latest version React Native ( v0.28.0 ) in this GitHub Repo . 我有一个Jest运行的例子,在这个GitHub Repo中运行最新版本的React Native( v0.28.0 )。

- -

After investigating this problem for quite some time, I finally found the solution. 经过一段时间调查这个问题后,我终于找到了解决方案。

There are a few online examples of React Native applications that are integrated with Jest, but you can't simply copy and paste the code into your codebase and expect it to work, unfortunately. 有一些与Jest集成的React Native应用程序的在线示例,但不幸的是,您不能简单地将代码复制并粘贴到代码库中并期望它能够正常工作。 This is because of RN version differences. 这是因为RN版本的差异。

Versions of React Native prior to v0.20.0 contained a .babelrc file in the packager ( node_modules/react-native/packager/react-packager/.babelrc ) that some online examples directly include it in their package.json . v0.20.0之前的React Native版本在v0.20.0包含一个.babelrc文件( node_modules/react-native/packager/react-packager/.babelrc ),一些在线示例直接将它包含在package.json However, versions v0.20.0 and up changed to no longer include this file which means that you can no longer attempt to include it. 但是, v0.20.0及更高版本v0.20.0更改为不再包含此文件,这意味着您无法再尝试包含该文件。 Because of this, I recommend using your own .babelrc file and definte your own presets and plugins. 因此,我建议使用您自己的.babelrc文件并对您自己的预设和插件进行definte。

I don't know what your package.json file looks like, but it's an incredibly important piece to solving this problem. 我不知道你的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"
    }
}

The other important piece is mocking React Native. 另一个重要的部分是模仿React Native。 I created a __mocks__/react-native.js file that looks like this: 我创建了一个__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;

By monkey patching the React Native functions like this, you can successfully avoid the weird Jest errors you get when trying to run your tests. 通过猴子修补这样的React Native函数,您可以成功避免在尝试运行测试时遇到的奇怪的Jest错误。

Finally, make sure you create a .babelrc file in your project's root directory that has, at the very least, these following lines: 最后,确保在项目的根目录中创建一个.babelrc文件,该文件至少包含以下几行:

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

This file will be used to tell babel how to correctly transform your ES6 code. 该文件将用于告诉babel如何正确转换您的ES6代码。

After following this setup, you should have no problems running Jest with React Native. 完成此设置后,使用React Native运行Jest应该没有问题。 I am confident that a future version of React Native will make it easier to integrate the two frameworks together, but this technique will work perfectly for the current version :) 我相信React Native的未来版本将更容易将两个框架集成在一起,但这种技术将完美适用于当前版本:)

EDIT 编辑

Instead of manually mocking the ReactNative elements in your __mocks__/react-native.js file, you can use the react-native-mock library to do the mocking for you (make sure to add the library to your package.json file): 您可以使用react-native-mock库为您进行模拟 (确保将库添加到package.json文件中),而不是手动模拟__mocks__/react-native.js文件中的ReactNative元素:

// __mocks__/react-native.js

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

I updated my GitHub Repo example to demonstrate this method. 我更新了我的GitHub Repo示例以演示此方法。

Did you tried to mock react-native inside of test file? 您是否尝试在测试文件中模拟react-native?

It works fine for my test case: 它适用于我的测试用例:

jest.dontMock(`../my-module-that-uses React-native.AsyncStorage`);

jest.setMock(`react-native`, {
  AsyncStorage: {
    multiGet: jest.fn(),
  },
});

const ReactNative = require(`react-native`);
const {AsyncStorage, } = ReactNative;

const myModule = require(`../my-module-that-uses React-native.AsyncStorage`);

// Do some tests and assert ReactNative.AsyncStorage.multiGet calls

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM