【问题标题】:Is it actually possible to make Detox/Jest tests pass with a React Native app running with Expo?使用 Expo 运行的 React Native 应用程序实际上可以通过 Detox/Jest 测试吗?
【发布时间】:2019-07-15 22:51:17
【问题描述】:

我正在尝试使用 Expo 为我的 React Native 应用程序创建一个自动化 UI 测试套件。我到处寻找好的教程,但是当我到达实际的测试编写部分时,由于环境问题,例如import Icon from... 上的“意外标识符/令牌”或其他我找不到任何教程的愚蠢问题,我的测试甚至从未运行过如何修复它们。我确实花了一周的时间试图解决这些问题。

我是 React Native 的新手,也是 Jest/Detox/Expo 的新手

这是我的 package.json

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "test": "node_modules/.bin/jest test/**/*.spec.js",
    "eject": "expo eject"
  },
  "jest": {
    "verbose": true,
    "preset": "jest-expo"
  },
  "dependencies": {
    "apsl-react-native-button": "^3.1.1",
    "react": "16.5.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
    "react-native-camera": "git+https://git@github.com/react-native-community/react-native-camera.git",
    "react-native-camera-roll-picker": "^1.2.3",
    "react-native-elements": "^1.0.0",
    "react-native-fontawesome": "^6.0.1",
    "react-native-is-iphonex": "^1.0.1",
    "react-native-vector-icons": "^6.2.0",
    "react-navigation": "^3.1.5"
  },
  "devDependencies": {
    "babel-preset-expo": "^5.0.0",
    "bunyan-debug-stream": "^2.0.0",
    "detox": "^10.0.9",
    "detox-expo-helpers": "^0.6.0",
    "expo-detox-hook": "^1.0.10",
    "jest-expo": "^32.0.0",
    "react-native-testing-library": "^1.5.0",
    "react-test-renderer": "^16.8.2",
    "babel-jest": "^24.1.0",
    "enzyme": "^3.9.0",
    "@babel/core": "^7.3.3",
    "@expo/vector-icons": "^9.0.0",
    "expo": "^32.0.0",
    "jest": "^24.1.0"
  },
  "private": true,
  "detox": {
    "test-runner": "jest",
    "configurations": {
      "ios.sim": {
        "binaryPath": "bin/Exponent.app",
        "type": "ios.simulator",
        "name": "iPhone X"
      }
    }
  }
}

这是我遇到的错误

ip-10-101-32-118:KitchenProject bob.dole$ detox test --loglevel trace
configuration="ios.sim" loglevel="trace" artifactsLocation="artifacts/ios.sim.2019-02-21 21-54-14Z" node_modules/.bin/jest "e2e" --config=e2e/config.json --maxWorkers=1 '--testNamePattern=^((?!:android:).)*$' 
● Deprecation Warning:

  Option "setupTestFrameworkScriptFile" was replaced by configuration "setupFilesAfterEnv", which supports multiple paths.

  Please update your configuration.

  Configuration Documentation:
  https://jestjs.io/docs/configuration.html

 FAIL  e2e/RoomLayout.spec.js
  ● Test suite failed to run

    /Users/bob.dole/KitchenDetail/KitchenProject/node_modules/@expo/vector-icons/FontAwesome.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import glyphMap from './vendor/react-native-vector-icons/glyphmaps/FontAwesome.json';
                                                                                                    ^^^^^^^^

    SyntaxError: Unexpected identifier

    > 1 | import FontAwesomeI from 'react-native-vector-icons/FontAwesome'
        | ^
      2 | import React from 'react'
      3 | 
      4 | export const FontAwesome = props => (

      at ScriptTransformer._transformAndBuildScript (../node_modules/jest/node_modules/jest-runtime/build/ScriptTransformer.js:440:17)
      at Object.<anonymous> (../Components/icons.js:1:1)

 FAIL  e2e/tests/components/RoomLayoutDetox.spec.js
  ● Test suite failed to run

    /Users/bob.dole/KitchenDetail/KitchenProject/node_modules/@expo/vector-icons/FontAwesome.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import glyphMap from './vendor/react-native-vector-icons/glyphmaps/FontAwesome.json';
                                                                                                    ^^^^^^^^

    SyntaxError: Unexpected identifier

    > 1 | import FontAwesomeI from 'react-native-vector-icons/FontAwesome'
        | ^
      2 | import React from 'react'
      3 | 
      4 | export const FontAwesome = props => (

      at ScriptTransformer._transformAndBuildScript (../node_modules/jest/node_modules/jest-runtime/build/ScriptTransformer.js:440:17)
      at Object.<anonymous> (../Components/icons.js:1:1)

Test Suites: 2 failed, 2 total
Tests:       0 total
Snapshots:   0 total
Time:        0.827s
Ran all test suites matching /e2e/i with tests matching "^((?!:android:).)*$".
child_process.js:677
    throw err;
    ^

Error: Command failed: node_modules/.bin/jest "e2e" --config=e2e/config.json --maxWorkers=1 '--testNamePattern=^((?!:android:).)*$' 
    at checkExecSyncError (child_process.js:637:11)
    at Object.execSync (child_process.js:674:13)
    at runJest (/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/detox/local-cli/detox-test.js:166:6)
    at run (/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/detox/local-cli/detox-test.js:86:7)
    at Object.<anonymous> (/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/detox/local-cli/detox-test.js:229:1)
    at Module._compile (internal/modules/cjs/loader.js:738:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:749:10)
    at Module.load (internal/modules/cjs/loader.js:630:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:570:12)
    at Function.Module._load (internal/modules/cjs/loader.js:562:3)

这是我的组件文件 RoomLayout.js

import React, { Component } from 'react';
import { StyleSheet, View, Text, Button } from 'react-native';
import { LayoutButtons } from './LayoutButtons';
import { CameraLauncher } from './CameraLauncher';
import { CommentsLauncher } from './CommentsLauncher';


export class RoomLayout extends Component {
  render() {
    return (
      <View>
        <Text testID='roomLayoutText' style={styles.room}>
          Room Layout{"\n"}
        </Text>
        <Text testID='infoText' style={styles.infoText}>
          Take photos from opposite corners of the room{"\n"}
        </Text>
          <LayoutButtons />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  view: {
      marginTop: 80, 
      textAlign: 'center',
      alignItems: 'center',
      justifyContent: 'center'
  },
  infoText: {
      marginTop: -10,
      fontWeight: 'normal',
      textAlign: 'center',
      fontSize: 12,
      justifyContent: 'center',
      alignSelf: 'center',
      color: 'grey'
  },
  room: {
      marginTop: 15,
      fontWeight: 'bold',
      textAlign: 'center',
      lineHeight: 14,
      fontSize: 15
  }
});

这是我的 RoomLayout.spec.js 文件

import React from 'react';
// import { RoomLayout } from '../Components/RoomLayout';
import { render } from 'react-native-testing-library';

describe('RoomLayout', () => {
   // *** EDIT - I have removed this code ***
   // beforeEach(async () => {
   //   const tree = render(<RoomLayout />);
   //
   // });

  test('should have header and info text', async () => {
      await element(by.text('Room Layout'));
      await element(by.id('infoText'));
      await element(by.id('infoText').and(by.text(' Take photos from opposite corners of the room')));
      await expect(element(by.id('layoutButtonsReference'))).toBeVisible();
    });
  });

【问题讨论】:

  • 你能解释一下这条线吗?为什么要使用它? const tree = render();
  • 我的错误,该行是由于尝试成功运行测试失败的结果。我已将其删除并重新运行 detox test --loglevel trace 并遇到相同的结果。
  • 我注意到的几件事是您没有在RoomLayout.js 中设置任何`testIDs`,并且组件中缺少文本'Room Layout''Take photos from opposite corners of the room'。关于您的图标,为什么不使用 @expo/vector-icons,因为 FontAwesome 包含在 Expo 中?
  • FontAwesomeIoncions 是不同的,它们有不同的图标。您不能只是切换它们并期望它们相同。你应该使用import { FontAwesome } from '@expo/vector-icons';你应该检查你的图标在目录expo.github.io/vector-icons
  • 不用担心。我冒昧地写了一篇关于如何在 expo 中使用 detox 的教程。如果您认为它有用,最好将其标记为已接受的答案。

标签: react-native jestjs font-awesome expo detox


【解决方案1】:

使用 Expo 应用程序设置排毒。您可能最适合从一个尚未完成任何操作的干净应用程序开始。您需要确保您已遵循基本设置 (step 1) 以便让排毒在您的机器上工作

#安装以下devDependencies

npm i -D detox detox-expo-helpers expo-detox-hook jest

#更新package.json

将以下内容添加到您的package.json 文件中,这将配置排毒。您可以选择所需的 iPhone 类型。

"detox": {
  "configurations": {
    "ios.sim": {
      "binaryPath": "bin/Exponent.app",
      "type": "ios.simulator",
      "name": "iPhone X"
    }
  },
  "test-runner": "jest"
 }

在脚本部分添加以下内容:

"scripts": {
  "e2e": "detox test --configuration ios.sim"
}

这将允许我们运行排毒测试,但使用npm run e2e

#设置你的第一个测试

运行以下命令来设置您的第一个测试

detox init -r jest

这将在您的项目中添加一个名为e2e 的文件夹。你会在里面找到三个文件

  • config.json
  • firstTest.spec.js
  • init.js

firstTest.spec.js 是一个样本测试。您需要对其进行以下更改。

const { reloadApp } = require('detox-expo-helpers');

您还需要更改以下行

await device.reloadReactNative();

await reloadApp();

#将 Expo 客户端添加到您的项目中

  • Expo.io/tools下载Expo Client iOS App。
  • 解压 iOS IPA 并将文件夹重命名Exponent.app。它会有一个文件图标,但仍然是一个文件夹。
  • 创建bin 文件夹并将Exponent.app 放入其中,使其与上面设置的binaryPath 匹配。

或者您也可以使用以下脚本,在您的项目根目录中创建一个文件并将其命名为setup.sh,复制内容然后运行它(您可能需要授予它运行权限,您可以通过先运行chmod +x setup.sh,然后你可以使用./setup.sh运行它。

#!/bin/bash -e

# query expo.io to find most recent ipaUrl
IPA_URL=`curl https://expo.io/--/api/v2/versions |  python -c 'import sys, json; print json.load(sys.stdin)["iosUrl"]'`

# download tar.gz
TMP_PATH=/tmp/exponent.tar.gz
wget -O $TMP_PATH $IPA_URL

# recursively make app dir
APP_PATH=bin/Exponent.app
mkdir -p $APP_PATH

# unzip tar.gz into APP_PATH
tar -C $APP_PATH -xzf $TMP_PATH

此脚本与上述步骤相同。

#运行你的第一个测试

expo start -c启动打包器

启动您计划用于测试的模拟器(因此,如果您选择了 iPhone X,请启动 iPhone X 等)。

如果添加了脚本,则可以在终端中运行npm run e2e,也可以运行detox test

你会发现你的测试会失败。这没关系,在意料之中。

现在,如果您想让您的测试通过,您需要实现 firstTest.spec.js 中存在的所有测试用例,或者您可以抓取这些并编写自己的测试用例。

#链接

  • 排毒自己的博览会Guide
  • 排毒setup
  • Expo sample app 排毒

#让你的测试通过

如果你想让你的测试通过,你可以更新以下文件,你应该得到 3 个通过测试。

firstTest.spec.js

const { reloadApp } = require('detox-expo-helpers');

describe('Example', () => {
  beforeEach(async () => {
    await reloadApp();
  });

  it('should have welcome screen', async () => {
    await expect(element(by.id('welcome'))).toBeVisible();
  });

  it('should show hello screen after tap', async () => {
    await element(by.id('hello_button')).tap();
    await expect(element(by.label('Hello!!!'))).toBeVisible();
  });

  it('should show world screen after tap', async () => {
    await element(by.id('world_button')).tap();
    await expect(element(by.label('World!!!'))).toBeVisible();
  });
});

App.js

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

export default class App extends Component {
  state = {
    greeting: undefined
  };

  render () {
    if (this.state.greeting) return this.renderAfterButton();
    return (
      <View
        testID="welcome"
        style={{
          flex: 1,
          paddingTop: 20,
          justifyContent: 'center',
          alignItems: 'center'
        }}>
        <Text style={{ fontSize: 25, marginBottom: 30 }}>Welcome</Text>
        <TouchableOpacity
          testID="hello_button"
          onPress={this.onButtonPress.bind(this, 'Hello')}>
          <Text style={{ color: 'blue', marginBottom: 20 }}>Say Hello</Text>
        </TouchableOpacity>
        <TouchableOpacity
          testID="world_button"
          onPress={this.onButtonPress.bind(this, 'World')}>
          <Text style={{ color: 'blue', marginBottom: 20 }}>Say World</Text>
        </TouchableOpacity>
      </View>
    );
  }
  renderAfterButton () {
    return (
      <View
        style={{
          flex: 1,
          paddingTop: 20,
          justifyContent: 'center',
          alignItems: 'center'
        }}>
        <Text style={{ fontSize: 25 }}>{this.state.greeting}!!!</Text>
      </View>
    );
  }

  onButtonPress (greeting) {
    this.setState({
      greeting: greeting
    });
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  }
});

【讨论】:

  • 老兄,非常感谢您在这方面的帮助!我现在有一个功能齐全且通过了 Detox 测试套件!
  • 你为什么在这里使用 by.label() 来测试文本?文档说按标签匹配可访问性标签
  • 我知道docs 是这么说的。虽然当我用.text 尝试它时,将它更改为.label 会失败。似乎没有一致性。公平地说,他们甚至在docs 中说最好匹配id。我不确定这是否是 Expo 问题,因为当我在完整的 react-native 应用程序中使用 Detox 时,.text 的问题会更少。试试看什么对你有用。
  • 你也看看DetoxExpo例子app这是他们test的样子。注意.label 的用法
  • 感谢您的回复。我正在学习这个库的所有小怪癖。
猜你喜欢
  • 2020-11-18
  • 2016-09-14
  • 2018-03-11
  • 2020-09-30
  • 2019-07-07
  • 2017-07-05
  • 1970-01-01
  • 2021-02-18
  • 2019-09-22
相关资源
最近更新 更多