排毒测试即使在添加 id 后也无法识别 View

detox tests not recognising View even after adding id

如果之前有人问过这个问题,请提前致歉。我偶然发现了 detox e2e 框架,用于响应本机应用程序,我想尝试一下。

我正在尝试自动化这里给出的这个演示移动应用程序 - link 由于 detox 中的测试使用 testID 作为定位器之一,因此我在 app/screen/LoginScreenMaterial.js 内的 LoginScreenMaterial.js 文件中添加了一个,如下所示

<View testID="login_screen" style={{width: this._width, justifyContent: 'center'}}>
          <RkCard style={styles.container}>
            <View rkCardHeader style={styles.header}>
              <RkText/>
              <RkText style={styles.label}>Sign in into your account</RkText>
            </View>

但是,即使在成功构建应用程序之后,我 运行 通过这个简单的测试

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

但是,测试仍然失败,无法识别元素。我在这个测试中缺少什么?我们能不能在 .js 文件中不这样显式地添加 testID

编辑 1:添加错误消息

1) Example
       should have welcome screen:
     Error: Error: Cannot find UI Element.
Exception with Assertion: {
  "Assertion Criteria" : "assertWithMatcher:matcherForSufficientlyVisible(>=0.750000)",
  "Element Matcher" : "(((respondsToSelector(accessibilityIdentifier) && accessibilityID('login_screen')) && !(kindOfClass('RCTScrollView'))) || (kindOfClass('UIScrollView') && ((kindOfClass('UIView') || respondsToSelector(accessibilityContainer)) && ancestorThatMatches(((respondsToSelector(accessibilityIdentifier) && accessibilityID('login_screen')) && kindOfClass('RCTScrollView'))))))",
  "Recovery Suggestion" : "Check if the element exists in the UI hierarchy printed below. If it exists, adjust the matcher so that it accurately matches element."
}

Error Trace: [
  {
    "Description" : "Interaction cannot continue because the desired element was not found.",
    "Error Domain" : "com.google.earlgrey.ElementInteractionErrorDomain",
    "Error Code" : "0",
    "File Name" : "GREYElementInteraction.m",
    "Function Name" : "-[GREYElementInteraction matchedElementsWithTimeout:error:]",
    "Line" : "124"
  }
]
      at Client.execute (node_modules/detox/src/client/Client.js:74:13)

使用 AccessiblityLabel 将 ID 添加到视图元素: https://facebook.github.io/react-native/docs/accessibility.html

实际上,这个 accessibilityLabel 将成为您的测试 ID,它将在您的测试工具中被识别:

<TouchableOpacity
  accessible={true}
  accessibilityLabel={'Tap me!'}
  onPress={this._onPress}>
  <View style={styles.button}>
    <Text style={styles.buttonText}>Press me!</Text>
  </View>
</TouchableOpacity>

我查看了该应用程序并使其能够正常工作。我在我的 devDependencies 中设置了以下内容。

  "devDependencies": {
    ...
    "jest": "23.2.0",
    "detox": "8.0.0"
    ...
  },

到package.json我还加了

"detox": {
    "configurations": {
      "ios.sim.debug": {
        "binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/BoomApp.app",
        "build": "xcodebuild -project ios/BoomApp.xcodeproj -scheme BoomApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
        "type": "ios.simulator",
        "name": "iPhone 7"
      }
    },

我运行detox init -r jest

然后我能够让它识别特定屏幕何时被渲染我通过将 testID 添加到 ScrollView LoginScreenBlur.js(第 23 行)

<AppWrapper>
    <ScrollView contentContainerStyle={{flex: 1}} testID={'login_screen'}>
    ....
    </ScrollView>
</AppWrapper>

A 然后在 e2e/firstTest.spec.js 我用

替换了测试
  it('should have loginScreen', async () => {
    await expect(element(by.id('login_screen'))).toBeVisible();
  });

这是我在 运行 detox build && detox test

之后的控制台响应
node_modules/.bin/jest e2e --config=e2e/config.json --maxWorkers=1 --testNamePattern='^((?!:android:).)*$'
 server listening on localhost:64579...
 : Searching for device matching iPhone 7...
 : Uninstalling org.reactjs.native.example.BoomApp...
 : org.reactjs.native.example.BoomApp uninstalled
 : Installing /Users/work/Downloads/react-native-ui-kitten-demo-app-master/ios/build/Build/Products/Debug-iphonesimulator/BoomApp.app...
 : /Users/work/Downloads/react-native-ui-kitten-demo-app-master/ios/build/Build/Products/Debug-iphonesimulator/BoomApp.app installed
 : Terminating org.reactjs.native.example.BoomApp...
 : org.reactjs.native.example.BoomApp terminated
 : Launching org.reactjs.native.example.BoomApp...
7: org.reactjs.native.example.BoomApp launched. The stdout and stderr logs were recreated, you can watch them with:
        tail -F /Users/work/Library/Developer/CoreSimulator/Devices/AF406169-5CF3-4480-9D00-8F934C420043/data/tmp/detox.last_launch_app_log.{out,err}
 PASS  e2e/firstTest.spec.js (7.935s)
  Example
    ✓ should have loginScreen (1499ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.87s, estimated 9s
Ran all test suites matching /e2e/i with tests matching "^((?!:android:).)*$".

应用程序似乎默认启动 LoginScreenBlur,因此首先测试它而不是 LoginScreenMaterial 是有意义的。

我注意到的一件事是该应用程序使用 RKTextInputRkButton,它们不是本机组件,而是本机组件的包装器。这意味着您需要将 testID 传递给您想要拥有该 testID 的本机组件。我不确定 react-native-ui-kitten 是否支持辅助功能标签,因此如果您希望自动输入文本和点击按钮,可能还有更多工作要做。

为自定义组件添加testID

参见步骤 3 https://github.com/wix/detox/blob/master/docs/Introduction.WritingFirstTest.md

Note that not all React components support this prop. Most of the built-in native components in React Native like View, Text, TextInput, Switch, ScrollView have support though. If you create your own composite components, you will have to propagate this prop manually to the correct native component.

自定义组件添加testID更详细的解释在这里https://github.com/wix/detox/blob/master/docs/Troubleshooting.RunningTests.md#cant-find-my-component-even-though-i-added-testid-to-its-props

简而言之,您应该按如下方式实现您的自定义组件。

自定义组件

export class MyCompositeComponent extends Component {
  render() {
    return (
      <TouchableOpacity testID={this.props.testID}>
        <View>
          <Text>Something something</Text>
        </View>
      </TouchableOpacity>
    );
  }
}

使用自定义组件

那你就这么用吧

render() {
  return <MyCompositeComponent testID='MyUniqueId123' />;
}

正在搜索层次结构

如果您已完成上述操作并且确定您的项目具有正确的 testID 并且测试仍然失败,那么您可以在视图层次结构中搜索它 https://github.com/wix/detox/blob/master/docs/Troubleshooting.RunningTests.md#debug-view-hierarchy

我不会完全重复上面的 post 但步骤是

  1. 在您的模拟器中启动可调试应用程序(不是发布版本)
  2. 打开Xcode
  3. 将 Xcode 附加到您的应用进程
  4. 按“调试视图层次结构”按钮
  5. 这将打开层次结构查看器,并显示您应用的本机视图层次结构的细分。在这里您可以浏览浏览量
  6. React Native testID 在本机视图层次结构中表现为可访问性标识符