Detox 是什么?
在创建新功能或修复错误后,我们通常会在我们的设备上安装该应用并手动测试。但是,当我们想要在标记版本之前验证所有新旧功能时,此过程可能会很麻烦。解放双手,是我们梦寐以求的。
Detox,一个用于测试 React Native 应用程序 Selenium 界面的端到端自动化测试工具。
Detox 是为移动端APP打造的灰盒端到端自动化测试框架。在 ReactNative 开发中,使用 js 测试框架 jest 或 mocha 来执行相应的测试脚本。通过对RN程序包中 package.json 增加 Detox 相关配置,并在RN程序包中增加测试案例文件夹 e2e(在其中增加测试案例)实现测试用例的编写和执行。
Detox 专注于同步(我们稍后会看到一个例子)例如添加 sleep(2000)等待后端完成请求,往往会在较慢的CI机器或网络上中断。使用Detox,以及谷歌开发的东西(适用于iOS的EarlGrey和适用于Android的Espresso),不仅可以自动等待完成请求,还可以等待动画,定时器等。
关于 Detox 更多的了解,大家可以看官方文档:Detox
同样分享一篇老外的分享:E2E Testing React Native with Detox + Screenshots
Detox 环境安装
Detox 的运行需要依赖以下插件环境
全局安装
1. Node.js (8.3.0 or higher)
brew update && brew install node
2. applesimutils
用于Apple模拟器的utils集合,Detox使用它与模拟器进行通信。
brew tap wix/brew
brew install applesimutils
3. detox-cli
Detox 命令行工具
npm install -g detox-cli
切到项目安装
4. detox
npm install detox --save-dev
5. 在 package.json 中添加 detox 配置
"detox": {
"configurations": {
"ios.sim.debug": {
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/example.app",
"build": "xcodebuild -project ios/example.xcodeproj -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"name": "iPhone 7"
}
}
}
6. detox 支持 jest、mocha 编写测试用例
Jest
npm install jest --save-dev
Mocha
npm install mocha --save-dev
Detox与Mocha和Jest没有紧密耦合,也没有与这个特定的目录结构紧密耦合。 两者都只是一个建议,很容易更换,而不涉及Detox本身的内部实现。
7. detox-cli 具有 detox init 初始化便捷命令,可自动完成第一次测试的设置。 根据第6步选择的测试运行器,运行以下命令之一
Jest
detox init -r jest
Mocha
detox init -r mocha
以上步骤执行完成后,detox 已成功配置到项目中。
文件结构分析
当执行了 detox init 命令后,detox-cli 会自动在项目根目录生成一个 e2e 文件夹,以 jest 为例,结构如下:
测试用例的代码我们会在firstTest.spec.js中编写。
describe('Example', () => {
beforeEach(async () => {
await device.reloadReactNative();
});
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.text('Hello!!!'))).toBeVisible();
});
it('should show world screen after tap', async () => {
await element(by.id('world_button')).tap();
await expect(element(by.text('World!!!'))).toBeVisible();
});
});
上述是detox帮我们自动生成的一段代码,describe相当于testsuit,后面跟的是testsuit名称(建议与项目名称一致),it 相当于testcase,后面跟的是testcase名称。
detox的每一个步测试方法都是 async 的, 例如当 hello_button 被点击之后,App 的各种线程, animation, 网络请求, 异步方法等等统统运行完毕,App 完全空闲的时候 tap 方法才会resolve。
因此当测试运行到第二步的时候, word_button 一定是处于可点击状态的, 不需要再用sleep或者wait方法来保证 word_button 的状态。 这就是 自动同步(Automatically synchronized 同步指测试脚本和 App 的执行是按预期顺序执行的)。
编写测试用例
device
await device.reloadReactNative();
设备在每个测试文件中全局可用,它可以控制当前连接的设备(目前仅支持模拟器)。detox 提供了丰富的api:
device.launchApp()
device.relaunchApp()
Deprecateddevice.terminateApp()
device.sendToHome()
device.reloadReactNative()
device.installApp()
device.uninstallApp()
device.openURL(url)
device.sendUserNotification(params)
device.sendUserActivity(params)
device.setOrientation(orientation)
device.setLocation(lat, lon)
device.setURLBlacklist([urls])
device.enableSynchronization()
device.disableSynchronization()
device.resetContentAndSettings()
device.getPlatform()
device.takeScreenshot(name)
device.pressBack()
Android Onlydevice.shake()
iOS Only
获取组件
通过by.id('xxx')获取组件,并执行相应的操作。获取组件的方式有如下几种:
by.id()
通过ID查找组件by.text()
通过文本查找组件by.label()
通过label查找组件 (只支持iOS)by.type()
通过组件类型查找组件 (不建议使用,Android 和 iOS 端类型不一致)by.traits() 不建议使用
- Advanced 组合使用,例如:element(by.id('welcome').and(by.text('welcome')
所以,大部分情况下我们都使用 by.id。by.id会对应于JSX中的testID属性。看下面的例子
<Text testID={'welcome'} style={styles.welcome}>Welcome to React Native!</Text>
await expect(element(by.id('welcome')))
查找组件后,我们可以模拟什么操作呢?
.tap()
.longPress()
.multiTap()
.tapAtPoint()
.tapBackspaceKey()
.tapReturnKey()
.typeText()
.replaceText()
.clearText()
.scroll()
.scrollTo()
.swipe()
.setColumnToValue()
iOS only.pinchWithAngle()
iOS only.setDatePickerDate()
iOS only
可见,detox为们提供了很丰富的模拟操作,点击、长按、双击、清除、滑动切换,滚动等等。以下是简单的例子:
await element(by.id('tappable')).tap();
await element(by.id('tappable')).longPress();
await element(by.id('tappable')).multiTap(3);
await element(by.id('tappable')).tapAtPoint({x:5, y:10});
await element(by.id('textField')).typeText('passcode');
await element(by.id('textField')).replaceText('passcode again');
await element(by.id('textField')).clearText();
await element(by.id('scrollView')).scroll(100, 'down');
await element(by.id('scrollView')).scroll(100, 'up');
await element(by.id('scrollView')).scrollTo('bottom');
await element(by.id('scrollView')).scrollTo('top');
await element(by.id('scrollView')).swipe('down');
await element(by.id('scrollView')).swipe('down', 'fast');
await element(by.id('scrollView')).swipe('down', 'fast', 0.5);
await expect(element(by.type('UIPickerView'))).toBeVisible();
await element(by.type('UIPickerView')).setColumnToValue(1,"6");
await element(by.type('UIPickerView')).setColumnToValue(2,"34");
detox 使用 Matchers 在您的应用中查找UI元素,使用动作模拟用户与这些元素的交互,使用 Expectations 来验证这些元素的值。expect 函数会验证某个值是否符合预期。
await expect(element(by.text('Hello!!!'))).toBeVisible();
同样,detox 提供了丰富的验证方式:
await expect(element(by.id('UniqueId204'))).toBeVisible();
await expect(element(by.id('UniqueId205'))).toBeNotVisible();
await expect(element(by.id('UniqueId205'))).toExist();
await expect(element(by.id('RandomJunk959'))).toNotExist();
await expect(element(by.id('UniqueId204'))).toHaveText('I contain some text');
await expect(element(by.id('UniqueId204'))).toHaveLabel('Done');
await expect(element(by.text('I contain some text'))).toHaveId('UniqueId204');
await expect(element(by.id('UniqueId533'))).toHaveValue('0');
构建执行用例
测试用例编写完成后,接下来就是要验证的时刻。detox-cli 提供了 build 来执行App的构建操作。还记得刚开始我们在package.json 中对 detox 的 ios.sim.debug 配置吗?没错,detox build 就是会去执行该配置下面的build。而我们也可以继续增加自己的配置,例如:ios.sim.release 等等。
构建
detox build --configuration ios.sim.debug (debug模式)
detox build --configuration ios.sim.release (release模式)
执行用例
detox test --configuration ios.sim.debug (debug模式)
detox test --configuration ios.sim.release (release模式)
⚠️注意事项
(1)版本问题:node >= 8.3.0
(2)testID 的问题:全局统一,及时在不同的页面不能用同一个testID
(3)当页面太长的时候,某些元素在页面的没有显示出来,这时需要 用swipe 或者是scroll 来模拟一个向上拖动页面的过程,使被测试的元素显示出来才能进行测试
(4)有时候 模拟器上的键盘连接到Mac 的键盘,默认隐藏 模拟器的键盘,需要手动调出 模拟器的键盘,不然 测试中一些需要输入文字的测试会挂。。。CI 上这个问题常见,一直想办法通过程序代码的方式使模拟器使用自己的键盘。
(5)react-navigation 路由导航,侧滑返回需要禁掉。
以上就是关于 React Native Detox 测试的内容,希望对你有所帮助。