在 Expo React Native 项目中使用 Detox 运行测试时出错

Posted

技术标签:

【中文标题】在 Expo React Native 项目中使用 Detox 运行测试时出错【英文标题】:Error running tests with Detox in Expo React Native project 【发布时间】:2020-11-18 01:58:29 【问题描述】:

当我尝试在 React Native Expo 项目中使用 detox 运行测试时,我收到以下错误:

detox[18834] WARN:  [Client.js/PENDING_REQUESTS] App has not responded to the network requests below:
  (id = -1000) isReady: 

That might be the reason why the test "Login workflow should have login screen" has timed out.

detox[18834] INFO:  Login workflow: should have login screen [FAIL]

 FAIL  e2e/firstTest.e2e.js (137.697 s)
  Login workflow
    ✕ should have login screen (120015 ms)

  ● Login workflow › should have login screen

    thrown: "Exceeded timeout of 120000 ms for a hook.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

       7 |   );
       8 |
    >  9 |   it('should have login screen', async () => 
         |   ^
      10 |     await expect(element(by.id('loginFormTitle'))).toBeVisible()
      11 |   );
      12 |

      at firstTest.e2e.js:9:3
      at Object.<anonymous> (firstTest.e2e.js:8:1)

detox[18833] ERROR: [cli.js] Error: Command failed: node_modules/.bin/jest --config e2e/config.json '--testNamePattern=^((?!:android:).)*$' --maxWorkers 1 e2e

我正在运行 iPhone 11 Pro 模拟器,并且 expo 应用程序已经在单独的服务器中运行。我的/bin 文件夹中还有一个Exponent.app,这是我从Expo 网站下载的。我的测试用例中的逻辑不需要任何超时,它只涉及一个简单的登录屏幕。

这个错误有什么解决办法吗?

【问题讨论】:

【参考方案1】:

我在使用最新版本的 Expo (v39) 时遇到了类似的问题。

问题似乎在于,Detox 在运行测试之前会等待应用程序就绪事件,但这不会在最新版本的 Expo SDK 中触发。

我最终得到的解决方案是创建应用的独立版本并针对它运行 Detox。

我的.detoxrc.json 看起来像:


  ...,
  "configurations": 
    "ios": 
      "type": "ios.simulator",
      "build": "expo build:ios -t simulator",
      "binaryPath": "bin/myapp.app",                                                                                                            
    
  

【讨论】:

那么构建二进制文件而不是下载它? @conor909 是的,它会触发 Expo 服务器上的二进制构建过程,这需要一些时间。我厌倦了等待它运行并最终编写了自己的构建脚本,该脚本利用turtle-cli(另一个 Expo 工具)来构建二进制文件。它的速度要快得多。【参考方案2】:

截至 2020 年 12 月,我将 detox 17.14.3 与 Expo 39.0.5 一起使用,并且无需使用独立版本即可解决此问题。下面提供了补丁和解释。

原来detox-expo-helpers 覆盖了一个环境变量(特别是SIMCTL_CHILD_DYLD_INSERT_LIBRARIES)来指定ExpoDetoxHook 框架的路径(由expo-detox-hook 包提供)。好吧,对环境的更新现在在周期中发生得太晚了。它发生在运行 Detox 的 reloadApp 时,但到那时,Detox 已经启动了 Jest,它让工作人员使用自己的 process.env 副本运行。在创建时 get a copy 的 process.env 的工作人员,并且它们不与父进程共享,因此该库对环境变量所做的更改不会反映在 Jest 工作人员内部。正在运行的应用程序无法使用钩子框架,因此 Detox 卡在等待未到来的就绪信号。

Detox 的 iOS 模拟器 launchApp 使用 SIMCTL_CHILD_DYLD_INSERT_LIBRARIES 指定它自己的库,但由于环境变量在工作器中是隔离的,它看不到这个包所做的更改。为了解决这个问题,我修改了这个库,将对 process.env 的更改作为一个单独的导出函数公开,然后我在测试生命周期的更早的时候调用它,以确保在启动任何工作程序之前设置它。这是一个兼容patch-package的补丁。

# file patches/detox-expo-helpers+0.6.0.patch

diff --git a/node_modules/detox-expo-helpers/index.js b/node_modules/detox-expo-helpers/index.js
index 864493b..3147a55 100644
--- a/node_modules/detox-expo-helpers/index.js
+++ b/node_modules/detox-expo-helpers/index.js
@@ -45,7 +45,16 @@ function resetEnvDyldVar(oldEnvVar) 
   
 
 
-const reloadApp = async (params) => 
+let initialized = false;
+let detoxVersion;
+let oldEnvVar;
+const init = () => 
+  if (initialized) 
+    return;
+  
+
+  initialized = true;
+
   if (!fs.existsSync(expoDetoxHookPackageJsonPath)) 
     throw new Error("expo-detox-hook is not installed in this directory. You should declare it in package.json and run `npm install`");
   
@@ -56,12 +65,16 @@ const reloadApp = async (params) => 
     throw new Error ("expo-detox-hook is not installed in your osx Library. Run `npm install -g expo-detox-cli && expotox clean-framework-cache && expotox build-framework-cache` to fix this.");
   
 
-  const detoxVersion = getDetoxVersion();
-  const oldEnvVar = process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES;
+  detoxVersion = getDetoxVersion();
+  oldEnvVar = process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES;
 
   if (semver.gte(detoxVersion, '9.0.6')) 
     process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES = expoDetoxHookFrameworkPath;
   
+
+
+const reloadApp = async (params) => 
+  init();
 
   const formattedBlacklistArg = await blacklistCmdlineFormat(params && params.urlBlacklist);
   const url = await getAppUrl();
@@ -121,5 +134,6 @@ module.exports = 
   getAppUrl,
   getAppHttpUrl,
   blacklistLiveReloadUrl,
+  init,
   reloadApp,
 ;

有了这个,我修改了我的e2e/environment.js 文件,如下所示。添加的是initExpo 调用。当它在测试生命周期的早期运行时,环境会在任何工作程序启动之前被修改,因此,对reloadApp 的调用不再无限期地等待。

/* eslint-disable import/no-extraneous-dependencies */
const  init: initExpo  = require('detox-expo-helpers');
const  DetoxCircusEnvironment, SpecReporter, WorkerAssignReporter  = require('detox/runners/jest-circus');

class CustomDetoxEnvironment extends DetoxCircusEnvironment 
  constructor(config) 
    super(config);

    initExpo();

    // Can be safely removed, if you are content with the default value (=300000ms)
    this.initTimeout = 300000;

    // This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
    // This is strictly optional.
    this.registerListeners(
      SpecReporter,
      WorkerAssignReporter,
    );
  


module.exports = CustomDetoxEnvironment;

【讨论】:

不错!您是否向图书馆的 github 提交了 PR,以便此修复将其纳入未来的版本?

以上是关于在 Expo React Native 项目中使用 Detox 运行测试时出错的主要内容,如果未能解决你的问题,请参考以下文章

在 Expo React Native 项目中使用 Detox 运行测试时出错

react-native-permissions 在 react-native expo 项目中返回 RNPermissions null

如何使用搜索文本输入(expo、react-native)在屏幕上显示项目

无法在 react native expo 项目中构建 android apk

React Native Expo 超时错误

我可以在 React Native 中将 Zebra 扫描仪包与 Expo 项目一起使用吗?