用腳教你使用Appium自動測試你的React Native

語言: CN / TW / HK

theme: smartblue highlight: a11y-dark


| Appium[ æpi'ʌm ]

Appium是一款開源測試工具,可以用來測試安卓/iOS/Windows端的原生應用和Web混合應用。

工作原理
Appium有一個客戶端-伺服器架構。Appium客戶端向Appium伺服器(一個Node.js HTTP伺服器)傳送請求,反過來,Appium伺服器向執行動作的裝置傳送請求。然後,伺服器將結果返回給客戶端,讓你知道測試的狀態。 image.png

功能

  • 跨平臺
  • 多開發語言支援
  • 不需要重新編譯應用程式
  • 不需要重複找輪子,可共用API
  • 市場佔有率TOP1

優點

  1. 方便的切換測試原生應用或App內嵌的web頁面,對Hybrid App友好
  2. 使用各個平臺自身提供的測試框架,因此無需引入第三方程式碼或重新打包應用
  3. 開源,維護頻率很高,社群也有相對較高的活躍度
  4. 支援通過自定義外掛尋找元素

缺點

  • 不支援單元測試、跨應用測試
  • 測試效率相對較低

使用

預設您已經安裝node以及對應的Android或IOS等相關環境

使用appium有兩種方式:
方式1:npm
方式2:下載Appium Desktop,這是一種基於圖形的、基於桌面的方式來啟動 Appium 伺服器。

這裡主要介紹npm的使用方式

step 1: 安裝appium

npm install -g appium

step 2: 安裝webdriverio

npm install --save webdriverio @wdio/cli
webdriverio是什麼?
基於Node.js的下一代瀏覽器和移動自動化測試框架
生成 Web 驅動程式配置檔案以在測試時應用配置

step 3: 配置webdriverio

執行npx wdio config
image.png 生成的檔案目錄如下
image.png
wdio.conf.js配置檔案
複製並修改有註釋的欄位到你的wdio.config.js檔案
javascript exports.config = { port: 4723, services: [['appium', {command: 'appium'}]], runner: 'local', specs: ['./test/specs/**/*.js'], // 測試目錄 maxInstances: 1, capabilities: [ { maxInstances: 1, browserName: '', appiumVersion: '1.22.3', // appium -v 獲取 platformName: 'Android', platformVersion: '9', // 安卓版本 deviceName: 'Nexus_S_API_28', // 裝置名稱 執行adb -s emulator-5554 emu avd name 獲取 app: './android/app/build/outputs/apk/debug/app-debug.apk', // apk目錄, 沒有的話先在Android studio build一個 automationName: 'UiAutomator2', }, ], logLevel: 'trace', bail: 0, waitforTimeout: 10000, connectionRetryTimeout: 90000, connectionRetryCount: 3, framework: 'mocha', reporters: ['dot'], mochaOpts: { ui: 'bdd', timeout: 60000, }, };

step 4: 建立一個測試用例

修改test/specs/example.e2e.js檔案 ```javascript describe('Simple App testing', () => { beforeEach(async () => { const app = await $('~app-root'); await app.waitForDisplayed(10000, false); });

it('Login test: valid case', async () => { const username = await $('~username'); await username.setValue('codemagic'); const password = await $('~password'); await password.setValue('nevercode');

const loginBtn = await $('~login');
await loginBtn.click();

await $('~loginstatus').waitForDisplayed(11000);
const status = await $('~loginstatus').getText();
expect(status).toHaveValueContaining('登入成功');

});

it('Login test: invalid case', async () => { await $('~username').setValue('nevercode'); await $('~password').setValue('codemagic');

await $('~login').click();

await $('~loginstatus').waitForDisplayed(11000);
const status = await $('~loginstatus').getText();
expect(status).toHaveValueContaining('未登入'); // 斷言https://webdriver.io/docs/api/expect-webdriverio

}); }); ```

step 5: 建立一個app頁面

如圖:
image.png
修改你的App.js ```javascript import React, {Component} from 'react'; import { TouchableHighlight, StyleSheet, Text, TextInput, View, } from 'react-native';

export default class App extends Component { constructor() { super(); this.state = { username: '', password: '', isLogined: false, }; }

inputChangeHandler = (value, name) => { this.setState({ [name]: value, }); };

login = () => { if ( this.state.username === 'codemagic' && this.state.password === 'nevercode' ) { this.setState({isLogined: true}); } else { this.setState({isLogined: false}); } };

render() { return ( 登入頁面 this.inputChangeHandler(text, 'username')} />

    <View style={LOCAL_STYLES.inputContainer}>
      <TextInput
        name="password"
        accessibilityLabel="password"
        secureTextEntry={true}
        style={LOCAL_STYLES.input}
        onChangeText={text => this.inputChangeHandler(text, 'password')}
      />
    </View>

    <Text accessibilityLabel="loginstatus">
      {this.state.isLogined ? '登入成功' : '未登入'}
    </Text>

    <TouchableHighlight
      style={LOCAL_STYLES.buttonContainer}
      accessibilityLabel="login"
      onPress={this.login}>
      <Text style={{color: 'white'}}>Login</Text>
    </TouchableHighlight>
  </View>
);

} }

const LOCAL_STYLES = StyleSheet.create({ wrapper: { flex: 1, alignItems: 'center', justifyContent: 'center', },

inputContainer: { borderBottomColor: '#AFAFAF', backgroundColor: '#FFFFFF', borderRadius: 10, borderBottomWidth: 1, marginBottom: 16, flexDirection: 'row', alignItems: 'center', width: '80%', borderColor: 'blue', borderWidth: 1, }, buttonContainer: { height: 45, width: 250, flexDirection: 'row', justifyContent: 'center', alignItems: 'center', marginBottom: 20, borderRadius: 20, backgroundColor: '#00b5ec', }, }); ```

step 6: 編譯你的應用到模擬器

yarn android
yarn start

step 7: 執行測試用例

npx wdio ./wdio.conf.js appiumtest.gif 執行通過後會有passed的列印
**Spec Files: 1 passed, 1 total (100% completed) in 00:00:32**

至此,測試結束

Q&A

如何來標識RN元件?
對於 Android,新增accessibilityLabel屬性
對於 iOS,新增testID屬性
javascript <View style={LOCAL_STYLES.inputContainer}> <TextInput name="password" accessibilityLabel="password" testID='password' secureTextEntry={true} style={LOCAL_STYLES.input} onChangeText={(text) => this.inputChangeHandler(text, "password")} /> </View> 然後在測試指令碼通過$('~password')獲取
更多標識用法參考官方文件Selector

| 參考資料

# Appium自動化環境搭建保姆級教程
# Appium mobile testing – test React Native apps