React native 使用Detox模拟RNCamera不起作用,称为非模拟impl

React native 使用Detox模拟RNCamera不起作用,称为非模拟impl,react-native,jestjs,detox,React Native,Jestjs,Detox,我想在排毒测试中模仿你的镜头。但是,模拟摄影机不会覆盖实际实现 我听从并采纳了建议 结果是: 执行模拟摄影机类文件本身(打印顶级日志) 不调用模拟摄影机的takePictureAsync和render函数 使用Emulator的默认摄影机 我也尝试过: RN\u SRC\u EXT=e2e react native run android,然后进行排毒测试 添加“testrunner”:“jest”并在最佳正文中调用jest.mock` 将RN\u SRC\u EXT=e2e替换为RN\u

我想在排毒测试中模仿你的镜头。但是,模拟摄影机不会覆盖实际实现

我听从并采纳了建议

结果是:

  • 执行模拟摄影机类文件本身(打印顶级日志)
  • 不调用模拟摄影机的takePictureAsync和render函数
  • 使用Emulator的默认摄影机
我也尝试过:

  • RN\u SRC\u EXT=e2e react native run android
    ,然后进行排毒测试
  • 添加
    “testrunner”:“jest”并在最佳正文中调用
    jest.mock`
  • RN\u SRC\u EXT=e2e
    替换为
    RN\u SRC\u EXT=e2e.js
  • 清理:
    rm-rf节点_模块;纱线安装
我的应用程序布局如下(不包括与测试无关的内容):

package.json

{
  ...
  "dependencies": {
    ...
    "@types/jest": "^24.0.21",
    "react": "16.9.0",
    "react-dom": "latest",
    "react-native": "0.61.1",
    "react-native-camera": "^3.6.0",
  },
  "devDependencies": {
    "@babel/core": "latest",
    "@babel/preset-env": "latest",
    "@babel/register": "latest",
    "@babel/preset-react": "latest",
    "@react-native-community/eslint-config": "^0.0.5",
    "babel-jest": "^24.9.0",
    "detox": "^14.5.1",
    "jest": "^24.9.0",
    "jest-fetch-mock": "^2.1.2",
    "metro-react-native-babel-preset": "^0.56.0",
    "mocha": "^6.2.2",
    "react-test-renderer": "16.9.0"
  },
  "detox": {
    "configurations": {
      "android.emu.debug": {
        "binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
        "build": "RN_SRC_EXT=e2e cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
        "type": "android.emulator",
        "device": {
          "avdName": "Pixel_2_API_29"
        }
      },
      "android.emu.release": {
        "binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
        "build": "RN_SRC_EXT=e2e cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
        "type": "android.emulator",
        "device": {
          "avdName": "Pixel_2_API_29"
        }
      }
    }
  }
}
{
    "setupFilesAfterEnv": ["./init.js"],
    "testEnvironment": "node",
    "reporters": ["detox/runners/jest/streamlineReporter"],
    "testMatch": ["**/__tests__/**/*.js?(x)", "**/?(*.)(e2e).js?(x)"],
    "verbose": true
}
{
    "setupFilesAfterEnv": ["./init.js"],
    "testEnvironment": "node",
    "reporters": ["detox/runners/jest/streamlineReporter"],
    // tried with and without this line
    "testMatch": ["**/__tests__/**/*.js?(x)", "**/?(*.)(e2e).js?(x)"],
    "verbose": true
}
e2e/config.js

require('@babel/register')({
    //cache: true,
    presets: [require('metro-react-native-babel-preset')],
    plugins: [require('@babel/plugin-transform-runtime').default],
    only: ['./e2e', './js'],
    ignore: ['node_modules']
  });
const defaultSourceExts = require('metro-config/src/defaults/defaults').sourceExts
module.exports = {
    transformer: {
        getTransformOptions: async () => ({
            transform: {
                experimentalImportSupport: false,
                inlineRequires: false,
            },
        }),
    },
    resolver: { 
        sourceExts: process.env.RN_SRC_EXT
                    ? process.env.RN_SRC_EXT.split(',').concat(defaultSourceExts)
                    : defaultSourceExts
      }
};
// printed as: module.exports.resolver from e2e { sourceExts: [ 'e2e', 'js', 'json', 'ts', 'tsx' ] }
console.log("module.exports from e2e", module.exports);
const detox = require('detox');
const config = require('../package.json').detox;
const adapter = require('detox/runners/mocha/adapter');

before(async () => {
    await detox.init(config);
});

beforeEach(async function() {
    await adapter.beforeEach(this);
});

afterEach(async function() {
    await adapter.afterEach(this);
});

after(async () => {
    await detox.cleanup();
});
e2e/config.json

{
  ...
  "dependencies": {
    ...
    "@types/jest": "^24.0.21",
    "react": "16.9.0",
    "react-dom": "latest",
    "react-native": "0.61.1",
    "react-native-camera": "^3.6.0",
  },
  "devDependencies": {
    "@babel/core": "latest",
    "@babel/preset-env": "latest",
    "@babel/register": "latest",
    "@babel/preset-react": "latest",
    "@react-native-community/eslint-config": "^0.0.5",
    "babel-jest": "^24.9.0",
    "detox": "^14.5.1",
    "jest": "^24.9.0",
    "jest-fetch-mock": "^2.1.2",
    "metro-react-native-babel-preset": "^0.56.0",
    "mocha": "^6.2.2",
    "react-test-renderer": "16.9.0"
  },
  "detox": {
    "configurations": {
      "android.emu.debug": {
        "binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
        "build": "RN_SRC_EXT=e2e cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
        "type": "android.emulator",
        "device": {
          "avdName": "Pixel_2_API_29"
        }
      },
      "android.emu.release": {
        "binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
        "build": "RN_SRC_EXT=e2e cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
        "type": "android.emulator",
        "device": {
          "avdName": "Pixel_2_API_29"
        }
      }
    }
  }
}
{
    "setupFilesAfterEnv": ["./init.js"],
    "testEnvironment": "node",
    "reporters": ["detox/runners/jest/streamlineReporter"],
    "testMatch": ["**/__tests__/**/*.js?(x)", "**/?(*.)(e2e).js?(x)"],
    "verbose": true
}
{
    "setupFilesAfterEnv": ["./init.js"],
    "testEnvironment": "node",
    "reporters": ["detox/runners/jest/streamlineReporter"],
    // tried with and without this line
    "testMatch": ["**/__tests__/**/*.js?(x)", "**/?(*.)(e2e).js?(x)"],
    "verbose": true
}
e2e/mocha.opts

--recursive
--timeout 300000
--bail
--file e2e/init.js
--require e2e/config.js
--require e2e/config.json
--file e2e/react-native-camera.e2e.js
metro.config.js

require('@babel/register')({
    //cache: true,
    presets: [require('metro-react-native-babel-preset')],
    plugins: [require('@babel/plugin-transform-runtime').default],
    only: ['./e2e', './js'],
    ignore: ['node_modules']
  });
const defaultSourceExts = require('metro-config/src/defaults/defaults').sourceExts
module.exports = {
    transformer: {
        getTransformOptions: async () => ({
            transform: {
                experimentalImportSupport: false,
                inlineRequires: false,
            },
        }),
    },
    resolver: { 
        sourceExts: process.env.RN_SRC_EXT
                    ? process.env.RN_SRC_EXT.split(',').concat(defaultSourceExts)
                    : defaultSourceExts
      }
};
// printed as: module.exports.resolver from e2e { sourceExts: [ 'e2e', 'js', 'json', 'ts', 'tsx' ] }
console.log("module.exports from e2e", module.exports);
const detox = require('detox');
const config = require('../package.json').detox;
const adapter = require('detox/runners/mocha/adapter');

before(async () => {
    await detox.init(config);
});

beforeEach(async function() {
    await adapter.beforeEach(this);
});

afterEach(async function() {
    await adapter.afterEach(this);
});

after(async () => {
    await detox.cleanup();
});
e2e/config.json

{
  ...
  "dependencies": {
    ...
    "@types/jest": "^24.0.21",
    "react": "16.9.0",
    "react-dom": "latest",
    "react-native": "0.61.1",
    "react-native-camera": "^3.6.0",
  },
  "devDependencies": {
    "@babel/core": "latest",
    "@babel/preset-env": "latest",
    "@babel/register": "latest",
    "@babel/preset-react": "latest",
    "@react-native-community/eslint-config": "^0.0.5",
    "babel-jest": "^24.9.0",
    "detox": "^14.5.1",
    "jest": "^24.9.0",
    "jest-fetch-mock": "^2.1.2",
    "metro-react-native-babel-preset": "^0.56.0",
    "mocha": "^6.2.2",
    "react-test-renderer": "16.9.0"
  },
  "detox": {
    "configurations": {
      "android.emu.debug": {
        "binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
        "build": "RN_SRC_EXT=e2e cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
        "type": "android.emulator",
        "device": {
          "avdName": "Pixel_2_API_29"
        }
      },
      "android.emu.release": {
        "binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
        "build": "RN_SRC_EXT=e2e cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
        "type": "android.emulator",
        "device": {
          "avdName": "Pixel_2_API_29"
        }
      }
    }
  }
}
{
    "setupFilesAfterEnv": ["./init.js"],
    "testEnvironment": "node",
    "reporters": ["detox/runners/jest/streamlineReporter"],
    "testMatch": ["**/__tests__/**/*.js?(x)", "**/?(*.)(e2e).js?(x)"],
    "verbose": true
}
{
    "setupFilesAfterEnv": ["./init.js"],
    "testEnvironment": "node",
    "reporters": ["detox/runners/jest/streamlineReporter"],
    // tried with and without this line
    "testMatch": ["**/__tests__/**/*.js?(x)", "**/?(*.)(e2e).js?(x)"],
    "verbose": true
}
test.spec.js

describe('Example', () => {
  beforeEach(async () => {
      await device.reloadReactNative();
  });

  it('should blah blah balh', async () => {
      // test implementation
  });
 });
init.js

require('@babel/register')({
    //cache: true,
    presets: [require('metro-react-native-babel-preset')],
    plugins: [require('@babel/plugin-transform-runtime').default],
    only: ['./e2e', './js'],
    ignore: ['node_modules']
  });
const defaultSourceExts = require('metro-config/src/defaults/defaults').sourceExts
module.exports = {
    transformer: {
        getTransformOptions: async () => ({
            transform: {
                experimentalImportSupport: false,
                inlineRequires: false,
            },
        }),
    },
    resolver: { 
        sourceExts: process.env.RN_SRC_EXT
                    ? process.env.RN_SRC_EXT.split(',').concat(defaultSourceExts)
                    : defaultSourceExts
      }
};
// printed as: module.exports.resolver from e2e { sourceExts: [ 'e2e', 'js', 'json', 'ts', 'tsx' ] }
console.log("module.exports from e2e", module.exports);
const detox = require('detox');
const config = require('../package.json').detox;
const adapter = require('detox/runners/mocha/adapter');

before(async () => {
    await detox.init(config);
});

beforeEach(async function() {
    await adapter.beforeEach(this);
});

afterEach(async function() {
    await adapter.afterEach(this);
});

after(async () => {
    await detox.cleanup();
});
e2e/react native camera.e2e.js
:(from)


用排毒嘲弄和用玩笑嘲弄是两件不同的事情

为了能够用detox模拟第三方模块,您可以创建一个代理组件提供程序,它将决定使用哪个组件。这样您就可以加载模拟版本进行排毒测试

在您的情况下,您可以创建一个CameraProvider.js,它将只导出真实的RNCamera:

export { RNCamera } from 'react-native-camera';
然后,您需要在上一个文件旁边创建模拟版本CameraProvider.e2e.js,以便使用detox进行端到端测试:

import React from 'react';

const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));

export class RNCamera extends React.Component {
    static Constants = {
        Aspect: {},
        BarCodeType: {},
        Type: { back: 'back', front: 'front' },
        CaptureMode: {},
        CaptureTarget: {},
        CaptureQuality: {},
        Orientation: {},
        FlashMode: {},
        TorchMode: {},
        AutoFocus: { on: {} },
        WhiteBalance: { auto: {} },
    };

    takePictureAsync = async () => {
        console.log('mock RNCamera takePictureAsync');
        await timeout(2000);
        return { uri: './static-image.jpg' };
    };

    render() {
        console.log('mock RNCamera render()');
        return null;
    }
}

最后,如果您的metro bundler位于react本机项目的根文件夹中,您应该能够构建应用程序或启动bundler(用于调试目标)使用RN_SRC_EXT=e2e.js env变量,让metro bundler知道它应该替换哪些文件扩展名。

使用detox进行模拟和使用Jest进行模拟是两件不同的事情

为了能够用detox模拟第三方模块,您可以创建一个代理组件提供程序,它将决定使用哪个组件。这样您就可以加载模拟版本进行排毒测试

在您的情况下,您可以创建一个CameraProvider.js,它将只导出真实的RNCamera:

export { RNCamera } from 'react-native-camera';
然后,您需要在上一个文件旁边创建模拟版本CameraProvider.e2e.js,以便使用detox进行端到端测试:

import React from 'react';

const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));

export class RNCamera extends React.Component {
    static Constants = {
        Aspect: {},
        BarCodeType: {},
        Type: { back: 'back', front: 'front' },
        CaptureMode: {},
        CaptureTarget: {},
        CaptureQuality: {},
        Orientation: {},
        FlashMode: {},
        TorchMode: {},
        AutoFocus: { on: {} },
        WhiteBalance: { auto: {} },
    };

    takePictureAsync = async () => {
        console.log('mock RNCamera takePictureAsync');
        await timeout(2000);
        return { uri: './static-image.jpg' };
    };

    render() {
        console.log('mock RNCamera render()');
        return null;
    }
}

最后,如果您的metro bundler位于react本机项目的根文件夹中,您应该能够构建应用程序或启动bundler(用于调试目标)使用RN_SRC_EXT=e2e.js env变量,让metro bundler知道它应该替换哪些文件扩展名。

Gomino的答案是正确的,但缺少一点,即您需要从测试代码中的代理组件导入。

例如,在您的组件中执行此操作:

import { RNCamera } from '../proxies/RNCamera'; 
不是

import { RNCamera } from 'react-native-camera';
然后,您需要确保bundler是用RN_SRC_EXT=e2e.js env变量启动的。因此,我的解毒测试脚本(在package.json中)如下所示:

"start-detox-metro": "RN_SRC_EXT=e2e.js react-native start",
"detox-build": "detox build --configuration android.emu.debug",
"detox test": "detox test --configuration android.emu.debug --reuse",
按该顺序运行脚本


metro bundler将处理替换原始代理文件的.e2e.js文件(但仅当设置了env var“RN_SRC_EXT=e2e.js”时)。Gomino的回答是正确的,但缺少的是您需要从测试代码中的代理组件导入。

例如,在您的组件中执行此操作:

import { RNCamera } from '../proxies/RNCamera'; 
不是

import { RNCamera } from 'react-native-camera';
然后,您需要确保bundler是用RN_SRC_EXT=e2e.js env变量启动的。因此,我的解毒测试脚本(在package.json中)如下所示:

"start-detox-metro": "RN_SRC_EXT=e2e.js react-native start",
"detox-build": "detox build --configuration android.emu.debug",
"detox test": "detox test --configuration android.emu.debug --reuse",
按该顺序运行脚本

metro bundler将处理替换原始代理文件的.e2e.js文件(但仅当设置了env var“RN_SRC_EXT=e2e.js”时)