Тестирование приложений React Native с помощью Jest

Я без особого успеха пытался заставить Jest работать с моим собственным проектом. Кажется, что большинство потоков - это взломанные решения для запуска и работы, и я не могу преодолеть последнее препятствие, с которым я сталкиваюсь.

Проблема

Я получаю следующую ошибку при попытке запустить следующий фрагмент кода. Если я издеваюсь над response-native внутри файла jestSupport / env.js, я могу обойти ошибку, но, очевидно, я не могу использовать какие-либо структуры, такие как AsyncStorage, для фактического тестирования моего кода (так как у меня будет только фиктивная функциональность).

Вопрос

Есть ли у кого-нибудь предложения, как решить эту проблему?

На этом этапе я готов попробовать все, в том числе отказаться от всего, что связано с тестами, и попробовать еще раз. Если бы это было так, мне бы потребовался некоторый набор руководящих принципов, которым нужно следовать, поскольку документы React Native ужасно устарели относительно Jest, а я относительно новичок в сцене React.

Ошибка

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)

Тестовый код

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

/*
*  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;

Если вы дочитали до этого места, спасибо за уделенное время!

-Юльфы


person Yulfy    schedule 17.05.2016    source источник
comment
вам нужен "react-naitve.js" где-нибудь явно? (.Js будет ненужным и может вызвать проблемы)   -  person Daniel Schmidt    schedule 20.05.2016
comment
Просто дважды проверьте, чтобы быть уверенным, я случайно ничего не требую с .js.   -  person Yulfy    schedule 20.05.2016
comment
Возможно, реакция-нативный-макет (github.com/lelandrichardson/react-native-mock ) помогает вам. Может случиться так, что шутливый подход «имитируй все» не будет работать должным образом с RN.   -  person Daniel Schmidt    schedule 31.05.2016
comment
Спасибо за ссылку. Почти удручает то, что поддерживаемая среда тестирования для React Native, похоже, не работает. Сейчас я использую Tape для тестирования своих классов, но я попробую react-native-mock для компонентов реакции.   -  person Yulfy    schedule 31.05.2016
comment
фермент с мокко работает довольно хорошо   -  person Daniel Schmidt    schedule 31.05.2016


Ответы (2)


TL; DR

У меня есть рабочий пример Jest, работающего с последней версией React Native (v0.28.0) в этом Репозиторий GitHub.

-

После долгого изучения этой проблемы я наконец нашел решение.

Есть несколько онлайн-примеров приложений React Native, которые интегрированы с Jest, но, к сожалению, вы не можете просто скопировать и вставить код в свою кодовую базу и ожидать, что он будет работать. Это связано с различиями в версиях RN.

Версии React Native до v0.20.0 содержали .babelrc файл в упаковщике (node_modules/react-native/packager/react-packager/.babelrc), который некоторые онлайн-примеры напрямую включают в свои package.json. Однако в версии v0.20.0 и выше этот файл больше не включается, что означает, что вы больше не можете пытаться включить его. По этой причине я рекомендую использовать ваш собственный .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"
    }
}

Другая важная часть - издевательство над React Native. Я создал __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 Native, вы можете успешно избежать странных ошибок Jest, которые вы получаете при попытке запустить свои тесты.

Наконец, убедитесь, что вы создали файл .babelrc в корневом каталоге вашего проекта, который имеет, по крайней мере, следующие строки:

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

Этот файл будет использоваться, чтобы сообщить babel, как правильно преобразовать ваш код ES6.

После выполнения этой настройки у вас не должно возникнуть проблем с запуском Jest с React Native. Я уверен, что будущая версия React Native упростит интеграцию двух фреймворков вместе, но этот метод отлично подойдет для текущей версии :)

РЕДАКТИРОВАТЬ

Вместо того, чтобы вручную издеваться над элементами ReactNative в вашем __mocks__/react-native.js файле, вы можете использовать react-native-mock, чтобы сделать насмешку за вас (не забудьте добавить библиотеку в ваш package.json файл):

// __mocks__/react-native.js

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

Я обновил свой пример GitHub Repo, чтобы продемонстрировать этот метод.

person Abdullah Bakhsh    schedule 18.06.2016
comment
Просто чтобы вы не думали, что я игнорирую этот ответ, я протестирую его сегодня вечером. Спасибо за подробный ответ, то, что вы говорите, кажется логичным, поэтому мне интересно посмотреть, работает ли это. - person Yulfy; 19.06.2016
comment
Это отличное руководство, которое мне подходит. Я подозреваю, что здесь достаточно консолидированной информации, чтобы позволить другим пользователям заставить Jest работать с будущими версиями RN, даже если что-то изменится. Спасибо за ваш вклад! - person Yulfy; 20.06.2016
comment
Я рада, что помогло! Спасибо за тестирование. Теперь, когда у меня есть ваши отзывы, я сделаю эту информацию более общедоступной. - person Abdullah Bakhsh; 20.06.2016
comment
У вас есть пример с чистой шуткой (то есть без чая и энзима?) - person bschandramohan; 27.06.2016

Вы пытались имитировать реакцию на родную реакцию внутри тестового файла?

Он отлично работает для моего тестового примера:

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
person ColCh    schedule 31.05.2016