E2E-тестування в React Native з використанням Appium та Jest
Усім привіт! Мене звати Микита Парфенчук, я Intermediate React Native Engineer в SoftServe. Наразі почав працювати над додатком для автомобільного маркетплейсу. Пишемо його з нуля, невеличкою командою з трьох React Native-інженерів. Разом ми як розробляємо рішення, так і тестуємо їх.
У статті я поділюсь цікавим випадком з нашої практики, а саме: як ми впровадили Е2Е-тестування, як та чому для цього використовували Appium та Jest.
Суть питання
Для нашого проєкту ми вирішили запустити Е2Е-тестування, щоб в повному обсязі перевіряти поведінку та взаємодію додатку на різних платформах та середовищах. Писати такі тести не складно, але колегам було важко розібратися, як саме впровадити це рішення, адже ніде немає розгорнутих гайдів, як це зробити.
Ми почали шукати, як нам перевіряти роботу додатку, при цьому не уповільнюючи сам процес розробки, щоб паралельно писати Е2Е-тести і працювати над додатком. Оскільки на проєкті всі юніт-тести написані за допомогою Jest, то ми вирішили використати його і для Е2Е-тестів. Проте розберемося докладніше, до чого ж тут Appium.
Що таке Appium
Appium — це фреймворк з відкритим кодом, який використовується для автоматизованого тестування у кросплатформенній та мобільній розробці. Це чудовий інструмент для тестування UI у React Native. Для тестування і гібридної, і вебаплікації, Appium використовує оснований на вебдрайвері протокол для автоматизації різних модулів застосунку. Ми обрали Appium для нашого проєкту, оскільки він має ширші можливості для тестування, особливо у компоненті WebView, який ми також використовуємо.
Також є поширеним Detox-інструмент для end-to-end тестування, розроблений Wix. Detox використовується для «gray box» тестування, що відрізняє його від Appium, створеного для «black box».
Appium стане у нагоді у таких випадках:
- якщо тести написані командою QA-інженерів, а не самими розробниками;
- якщо ви плануєте писати тести не на JavaScript, а на іншій мові (бо Detox використовую саме JavaScript);
- якщо ви плануєте тестувати застосунок на реальних девайсах;
- якщо ви не збираєтесь (або за якимось причинами не можете) змінювати застосунок та вбудовані додаткові бібліотеки (це потрібно при використанні Detox).
Якщо ви React Native розробник і займаєтесь написанням тестів самотужки, можна сміливо використовувати Detox, бо він має гарну інтеграцію та високу швидкість виконання тестів.
Чим корисний Jest
Jest — це тестинговий фреймворк JavaScript, який створений для перевірки будь-якого коду JavaScript. З його допомогою ви можете створювати тести з доступним, зрозумілим та багатофункціональним API і отримувати швидкі результати. Для React Native, починаючи з версії React Native v. 0.38, Jest є встановленим за замовчуванням.
Розбираємося на практиці
Давайте спробуємо скомбінувати описані вище інструменти та почати використовувати їх для написання end-to-end тестів у React Native застосунках.
Prerequisite
- React Native >=0.64
Для того щоб не використовувати атрибут accessebilityLabel
для знаходження елементів в процесі автотестування, ми можемо визначити та використовувати testID
, починаючи з цієї версії React Native для Android.
Setup a new project
Давайте створимо новий React Native проєкт:
npx react-native init AppiumTest
Тепер нам необхідно встановити Appium через npm:
npm i appium --save-dev
Також, ми будемо використовувати тестовий фреймворк WebdriverIO для наших тестів, тому встановлюємо і його:
npm i webdriverio --save-dev
Далі створюємо кореневу папку проєкту, де буде відбуватися встановлення jest
та будуть знаходитися всі тести. Назвемо її e2e.
Appium and Jest setup
Ми хочемо виконувати тести на обох платформах, Android та iOS, тому давайте створимо jest.config.js
файл з двома проєктам для цих платформ, але з однаковими налаштуваннями.
const defaultOptions = { testEnvironment: './jest.environment.js', globalSetup: './jest.global-setup.js', globalTeardown: './jest.global-teardown.js', }; module.exports = { projects: [ {displayName: 'Android', ...defaultOptions}, {displayName: 'iOS', ...defaultOptions}, ], };
Сервер Appium буде стартувати перед всіма тестами. Тож визначимо це у файлі jest.global-setup.js
:
const appium = require('appium'); const startAppiumServer = () => { return appium.main({loglevel: 'none'}); }; const jestSetup = async () => { global.appium = await startAppiumServer(); }; module.exports = jestSetup;
Відповідно, він буде зупинений після виконання тестів. Наш jest.global-teardown.js
файл буде мати наступний вигляд:
const stopAppiumServer = () => { if (global.appium) { return global.appium.close(); } }; const jestTeardown = async () => { await stopAppiumServer(); process.exit(0); }; module.exports = jestTeardown;
Потім створюємо індивідуальне середовище, в якому ми будемо ініціювати web driver:
const NodeEnvironment = require('jest-environment-node'); const wdio = require('webdriverio'); const wdioOptions = { path: '/wd/hub/', port: 4723, }; class CustomEnvironment extends NodeEnvironment { constructor(config, context) { super(config, context); this.platform = config.displayName.name; } async setup() { await super.setup(); const capabilities = this.platform === 'Android' ? androidCapabilities : iOSCapabilities; const options = {...wdioOptions, capabilities}; const driver = await wdio.remote(options); this.global.driver = driver; } async teardown() { await super.teardown(); if (this.global.driver) { await this.global.driver.deleteSession(); } } } module.exports = CustomEnvironment;
Як ми бачимо, наш web driver використовує певні ресурси в залежності від платформи. Щоб запустити тести в Android-емуляторі, додайте цю змінну у файл:
const androidCapabilities = { platformName: 'Android', platformVersion: '10', deviceName: 'Pixel 4', avd: 'Pixel_4_API_29', // avd id app: './android/app/build/outputs/apk/release/app-release.apk', // relative to the project root appPackage: 'com.appiumtest', appWaitActivity: 'com.appiumtest.MainActivity', };
Для iOS-симулятора:
const iOSCapabilities = { platformName: 'iOS', platformVersion: '14.5', deviceName: 'iPhone 12', app: './ios/build/Build/Products/Release-iphonesimulator/AppiumTest.app', // relative to the project root };
Також ми використовуємо реліз білду для обох платформ. Вони будуть згенеровані перед запуском наших тестів. Давайте відкриємо package.json файл та зазначимо команди для запуску тестів:
"scripts": { "pree2e:android": "cd android && ./gradlew assembleRelease", "e2e:android": "jest -i --selectProjects Android --config=\"./e2e/jest.config.js\"", "pree2e:ios": "xcodebuild -workspace ./ios/AppiumTest.xcworkspace -configuration Release -scheme AppiumTest -sdk iphonesimulator -derivedDataPath ./ios/build", "e2e:ios": "jest -i --selectProjects iOS --config=\"./e2e/jest.config.js\"" }
Actual test
Як ми вказували раніше, починаючи з версії 0.64 React Native, атрибут testID
є доступним для Android-білдів, тож ми будемо використовувати його для того, щоб тегати елементи. Ми використовуємо Appium, де вимогою є використання package name як частини resource id. Ми маємо це враховувати, тому давайте створимо helper для встановлення testIDs.
Для отримання package name ми можемо встановити цю бібліотеку:
npm i react-native-device-info
Функція helper буде наступною:
import {Platform} from 'react-native'; import {getBundleId} from 'react-native-device-info'; const packageName = getBundleId(); export const tid = value => Platform.select({ android: `${packageName}:id/${value}`, ios: value, });
Давайте використаємо це в коді. Відкриваємо App.js файл та встановлюємо testID
для одного з view
. Наприклад:
import {tid} from './tid'; ... <SafeAreaView testID={tid('App')}> ...
Також нам необхідно додати той самий метод у середовище jest для використання його у тестах. Додаємо наступні рядки у файл jest.environment.js:
constructor(config, context) { ... this.tid = this.tid.bind(this); } tid(value) { if (!this.global.driver) { throw new Error('Appium driver is not initialized'); } return this.global.driver.isAndroid ? `${this.global.driver.capabilities.appPackage}:id/${value}` : value; } async setup() { ... this.global.tid = this.tid; } ...
Далі, створюємо теку __tests__ у e2e з App.spec.js тест-файлом та додаємо наступний код:
/* global driver, tid */ test('The App screen should be displayed', async () => { const screen = await driver.$(`id=${tid('App')}`); const isExisting = await screen.isExisting(); expect(isExisting).toBeTruthy(); });
Давайте перевіримо роботу на Android:
npm run e2e:android
Очікуваним результатом буде:
Тепер перевіримо на iOS:
npm run e2e:ios
Очікуваним результатом буде:
Troubleshooting
- Для перевірки, чи ваше локальне середовище має всі необхідні Appium- залежності, ви можете використовувати бібліотеку appium-doctor.
- Якщо у вас виникла помилка в процесі білду iOS, перевірте цей коментар у Xcode гайді з вирішення проблем.
6 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів