如何在 React Native 中截屏测试?
Posted
技术标签:
【中文标题】如何在 React Native 中截屏测试?【英文标题】:How do I screenshot tests in React Native? 【发布时间】:2015-10-22 15:43:21 【问题描述】:我想使用屏幕截图测试我的 React Native 应用程序。 UIAutomation javascript 文件将由fastlane 执行,并且应该向我提供我需要的所有子视图。这部分工作正常。
我的主要问题是我不明白如何点击一个元素。我发现的每个示例都是简单的objective-c,并使用标准元素进行导航,例如标签栏。我的应用程序有一个汉堡图标,它在TouchableHighlight
上有一个点击事件,它会打开一个菜单。我正在寻找一种可能性来引用单个 TouchableHighlight
element 以便与之交互。
这样的答案加分,我不用写 Objective-C。
【问题讨论】:
很好奇,你有没有找到“点击”这个 TouchableHighlight 的方法?我有一个非常相似的问题,很想听听你是否在这里取得了任何进展:) 很抱歉告诉您,但我已停止解决该问题:/ 如果您成功了,请分享您的见解 ;) Has anyone used UI Testing with react native?的可能重复 【参考方案1】:Fastlane(更具体的快照)已弃用 UI 测试的 UI 自动化。如果您需要更新 gem,您的 UIA javascript 将不适用于 UI 测试(使用 Obj C 或 Swift 编写)
为什么要改为 UI 测试?
UI 自动化已弃用 UI 测试将在未来发展并支持更多功能 UI 测试更容易调试 UI 测试是用 Swift 或 Objective C 编写的 UI 测试可以以更干净、更好的方式执行
https://github.com/fastlane/snapshot
看起来使用 React Native 的其他人在 UI 测试和快照方面取得了一些进展:https://github.com/fastlane/snapshot/issues/267
【讨论】:
【参考方案2】:我不熟悉 fastlane,但您可能想尝试一下 Jest,因为它已得到官方支持。诚然,他们并没有完全覆盖,而且在某些情况下,你很有可能不得不推出自己的解决方案,因为 react native 还很年轻,但这应该会让你从正确的角度开始Snapshot Tests (ios only)
【讨论】:
【参考方案3】:注意:我们使用 detox 进行测试,所以我使用 device.getPlatform()
来测试 iOS 或 android。
我最终做的是混合使用 JavaScript 库(pixelmatch 和 pngjs),使用 fs 和使用命令行命令(xcrun simctl
和 adb
)。
const device = require('detox');
const execSync = require('child_process');
const fs = require('fs');
const existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync = fs;
const PNG = require('pngjs').PNG;
const pixelmatch = require('pixelmatch');
const IOS_SCREENSHOT_OPTIONS =
timeout: 1000,
killSignal: 'SIGKILL'
;
function getScreenShotDirectory() ...
function getActualFileName(testName) ...
function getExpectedFileName(testName) ...
function getDiffFileName(testName) ...
async function takeScreenshot(testName)
const actualFileName = getActualFileName(testName);
const directoryName = getScreenShotDirectory();
if (!existsSync(directoryName))
mkdirSync(directoryName, recursive: true);
if (device.getPlatform() === 'ios')
execSync(`xcrun simctl io booted screenshot "$actualFileName"`, IOS_SCREENSHOT_OPTIONS);
await removeIosStatusBar(actualFileName);
else
execSync(`adb exec-out screencap -p > "$actualFileName"`);
const compareScreenshot = async testName =>
const actualFileName = getActualFileName(testName);
await takeScreenshot(testName);
const expectedFileName = getExpectedFileName(testName);
const actualImage = PNG.sync.read(readFileSync(actualFileName));
if (!existsSync(expectedFileName))
console.warn(`No expected image for $testName @ $expectedFileName`);
return false;
const expectedImage = PNG.sync.read(readFileSync(getExpectedFileName(testName)));
const width, height = actualImage;
const diffImage = new PNG(width, height);
const numDiffPixels = pixelmatch(actualImage.data, expectedImage.data, diffImage.data, width, height);
if (numDiffPixels === 0)
unlinkSync(actualFileName);
return true;
else
const percentDiffPixels = numDiffPixels / (width * height);
console.warn(
`Images are different $testName numDiffPixels=$numDiffPixels percentDiffPixels=$percentDiffPixels`
);
writeFileSync(getDiffFileName(testName), PNG.sync.write(diffImage));
return false;
;
为了改善您的测试结果,您应该使用 Android 的demo mode,例如:
execSync('adb shell settings put global sysui_demo_allowed 1');
execSync('adb shell am broadcast -a com.android.systemui.demo -e command ...');
execSync('adb shell am broadcast -a com.android.systemui.demo -e command exit');
从 xcode 11 你有:
execSync('xcrun simctl status_bar <device> override ...')
我使用以下代码从 iOS 中删除了状态栏(但这会降低性能):
const IOS_STATUS_BAR_HEIGHT = 40;
async function removeIosStatusBar(imageFileName)
return new Promise((resolve, reject) =>
const image = PNG.sync.read(readFileSync(imageFileName));
let width, height = image;
height -= IOS_STATUS_BAR_HEIGHT;
const dst = new PNG(width, height);
fs.createReadStream(imageFileName)
.pipe(new PNG())
.on('error', error => reject(error))
.on('parsed', function ()
this.bitblt(dst, 0, IOS_STATUS_BAR_HEIGHT, width, height, 0, 0);
dst
.pack()
.pipe(fs.createWriteStream(imageFileName))
.on('error', error => reject(error))
.on('finish', () => resolve(imageFileName));
);
);
【讨论】:
【参考方案4】:创建一个新项目。
$ react-native -v
react-native-cli: 2.0.1
$ react-native init NativeSnapshots
$ cd NativeSnapshots
$ react-native run-ios
测试它是否有效,启动欢迎屏幕。
$ cd ios
$ fastlane snapshot init
快车道输出:
[14:37:56]: For more information, check out https://docs.fastlane.tools/getting-started/ios/setup/#use-a-gemfile
✅ Successfully created SnapshotHelper.swift './SnapshotHelper.swift'
✅ Successfully created new Snapfile at './Snapfile'
-------------------------------------------------------
Open your Xcode project and make sure to do the following:
1) Add a new UI Test target to your project
2) Add the ./fastlane/SnapshotHelper.swift to your UI Test target
You can move the file anywhere you want
3) Call `setupSnapshot(app)` when launching your app
let app = XCUIApplication()
setupSnapshot(app)
app.launch()
4) Add `snapshot("0Launch")` to wherever you want to create the screenshots
More information on GitHub: https://github.com/fastlane/fastlane/tree/master/snapshot
第 1 步:将新的 UI 测试目标添加到您的项目中
Xcode 版本 8.3.3 > 打开 NativeSnapshots.xcodeproj
File > New > Target > iOS UI Testing Bundle
第 2 步:将 ./fastlane/SnapshotHelper.swift 添加到您的 UI 测试目标中
Highlight NativeSnapshotsUITests
File > Add Files to NativeSnapshots
Select ./fastlane/SnapshotHelper.swift, Enter
第 3 步:启动应用时调用 setupSnapshot(app)
在 Xcode 中打开 NativeSnapshotsUITests/NativeSnapshotsUITests.swift
。
替换:
XCUIApplication().launch()
与:
let app = XCUIApplication()
setupSnapshot(app)
app.launch()
第 4 步:将snapshot("0Launch")
添加到您要创建屏幕截图的任何位置
在 UI 测试的 testExample() 中添加快照调用。
func testExample()
snapshot("0Launch")
编辑 Snapfile 以避免出现巨大的矩阵。
devices([
"iPhone 6"
])
languages([
"en-US"
])
scheme "NativeSnapshots"
应该准备好了。
$ cd ios && fastlane snapshot
复制自aj0strow
【讨论】:
以上是关于如何在 React Native 中截屏测试?的主要内容,如果未能解决你的问题,请参考以下文章