E2E-тестування в React Native з використанням Appium та Jest

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Усім привіт! Мене звати Микита Парфенчук, я 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 гайді з вирішення проблем.

References

👍НравитсяПонравилось12
В избранноеВ избранном6
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Величезне дякую за таку деталізовану технічну статтю!
Репостнув в QA Club Lviv 😘

Detox чудово справляється з реальними Android приcтроями через:
— USB
— WiFI
— Samsung Remote Test Lab
Те що ви не змогли це налаштувати — то ваші справи 😎

А от iOS пристрої працюють всюди через костилі XCUITest/XCTest (така політика Apple) і Detox і Appium — не є виключеннями і не мають поблажок від Apple

В моєму випадку і значно простіше було підняти тести на Detox, ніж на Appium.
І Detox працював швидше.
Тому порівння в цій статті дещо сумнівним виглядає.

Це не порівняльна стаття Detox і Appium, де висновком є використання Appium. Навпаки, я вказав, що:

якщо ви React Native розробник і займаєтесь написанням тестів самотужки, можна сміливо використовувати Detox, бо він має гарну інтеграцію та високу швидкість виконання тестів

Стаття буде корисною розробникам, які з різних причин (можливі причини також вказані на початку статті), вирішили використовувати саме Appium замість Detox.

Я не вважаю костилем офіційний драйвер від Appium: appium.io/...​ios-xcuitest-real-devices. Саме тому, на мою думку, Appium краще підходить для тестування на реальних пристроях, тому що він підтримує як Android, так і iOS.

Я вам дуже не раджу розповсюджувати таку думку))
Повірте на великих проєктах, додатках, компаніях, все одно приходять до того що автотести починають розвивати окремо для iOS та Android набори тестів і воно все ще й повільно, може бути і нестабільно
В одиниць з сотень виходить вжитися з апіумом)

Аппіум хорош для малої кількості e2e тестів, не більше) Для всього іншого є нативні інтрументи автоматизації, і краще може бути ровивати автотести окремо одразу, аніж пхати і котлети і яйця в одну корзину

Подписаться на комментарии