Нативне тестування чи Appium: який з підходів обрати для мобільних застосунків

💡 Усі статті, обговорення, новини про тестування — в одному місці. Приєднуйтесь до QA спільноти!

Всім привіт! Мене звати Євгеній Блинний, я Automation QA у продуктовій компанії OBRIO з екосистеми Genesis. Наша команда розвиває продукт Nebula — найбільший бренд у ніші духовного самопізнання, який має понад 60 мільйонів користувачів з усього світу.

Свій шлях починав як Manual QA, але згодом вирішив перейти в автоматизацію. За понад сім років я встиг попрацювати з вебом і мобільними застосунками, використовуючи як Appium, так і нативну автоматизацію (iOS та Android). У цій статті хочу порівняти ці два підходи, розібрати їхні переваги та недоліки, а також поділитися робочими кейсами й рішеннями, які знаходив для усунення проблем. Усі висновки ґрунтуються на моєму особистому досвіді в різних проєктах.

Чим відрізняються нативна автоматизація та Appium

Нативна автоматизація — це тести, які пишуться разом із застосунком, котрий ви будете тестувати. Увесь код для тестування «живе» і запускається разом із застосунком. У такому випадку немає потреби збирати нову версію продукту окремо, завантажувати її кудись та передавати. Простими словами, ми можемо написати тест, який викликає запуск застосунку і його тестування.

Appium — це стороннє open source рішення, яке дає змогу взаємодіяти із застосунком через нативні драйвери. Це своєрідний прошарок між кодом та застосунком (драйвером). Тести, написані із використанням Appium, у більшості випадків розміщуються в окремому репозиторії. І перед початком тестування необхідно зібрати застосунок та «прокинути» на пристрій/симулятор/емулятор або інше місце, де тести зможуть його дістати.

Переваги Appium

Подібність до Selenium

Appium використовує WebDriver API, що робить його дуже схожим на Selenium за підходом до автоматизації. Хоча ці два інструменти працюють із різними типами застосунків (Appium — з мобільними, а Selenium — з веб), вони мають спільну архітектуру та концепції. Отже, якщо ви вже працювали з Selenium, писати тести для мобільних застосунків буде досить легко. Звісно, між ними є розбіжності — наприклад, різні типи локаторів або нюанси роботи з очікуваннями та очищенням даних. Але їх не так багато, і загалом підходи схожі.

Кросплатформеність

Однією з провідних переваг Appium є його кросплатформеність. За допомогою цього інструмента ви можете автоматизувати тестування як для Android, так і для iOS, незалежно від мов та фреймворків, якими написані застосунки — Swift, Kotlin, Java, Flutter, React Native тощо. Крім того, Appium не залежить від використовуваних компонентів інтерфейсу, особливо на Android, де застосунки можуть бути побудовані як на view-based підході, так і на Jetpack Compose. Теоретично можна створити фреймворк, який буде виконувати однакові тести для різних платформ, а різниця полягатиме лише в локаторах чи id елементів.

Однак на практиці не завжди так просто. Застосунки на різних платформах можуть виглядати однаково, але при цьому мати різну логіку взаємодії з елементами, різні стандартні сповіщення. Залежно від ОС відрізняється робота з нативними діалогами. Також в одному застосунку може бути баг, а в іншому — ні. Отже, теоретично написати один тест для різних платформ можливо, але на практиці доведеться вирішувати чимало проблем.

Одна мова програмування

Для написання тестів достатньо знати одну мову програмування, яку підтримує Appium. Ви можете попросити розробників зібрати для вас застосунок (або налаштувати CI/CD) й одразу почати автоматизувати.

Незалежність

Appium майже не обмежує вас у виборі рішень: можна використовувати будь-які бібліотеки, писати додаткові скрипти та запускати їх зі свого коду. Немає потреби щоразу збирати застосунок у разі будь-яких змін у тестах. До того ж ви не прив’язані до розробників: ваш код може мати власний стиль, підходи до рев’ю тощо. Ви керуєте репозиторієм і працюєте з ним на свій розсуд. Для досвідчених автоматизаторів це плюс, але для новачків усе ж таки краще, щоби хтось допомагав з рев’ю та порадами.

Доступ до додаткових функцій

Appium дає змогу одночасно взаємодіяти з іншими інструментами. Наприклад, ви можете відкрити браузер паралельно з тестуванням застосунку (якщо підключити Selenium чи іншу бібліотеку). Це може бути корисно для роботи з адмінками або перевірки інтеграції з вебверсією застосунку. Також можна використовувати додаткові можливості, як-от запуск певних дій до або після тестів, що може бути реалізовано «з коробки» і не потребує додаткових зусиль.

Усі ці переваги можна також реалізувати і в нативному тестуванні. Проте, коли тести не є частиною застосунку, працювати з ними простіше.

Недоліки Appium

Повільність виконання тестів

Appium є додатковим прошарком між застосунком і кодом. Щоразу, коли ви взаємодієте з елементом, Appium робить знімок і парсить його у свій формат, а потім вже віддає вам потрібний елемент. Це займає час.

Приклад із мого досвіду: коли я писав тести для досить простого застосунку, проблема зі швидкістю не була помітною, навіть коли кількість тестів сягала 100–200. Однак на іншому проєкті, куди я згодом перейшов, це стало проблемою. Застосунок був великим, і тести тривали по 20 хвилин, залежно від сценарію. Наприклад, тест відкривав екран, шукав батьківський елемент, а потім знаходив і натискав кнопку на ньому. Оскільки застосунок був навантажений, це займало багато часу. На натискання кнопки йшло близько хвилини, і ще стільки ж — на пошук елемента. В результаті повний прогін кількох тисяч тестів тривав понад 15 годин без урахування перезапуску провалених тестів і паралелізації на 15 потоків (ці значення приблизні).

Час на пошук елемента

Цей пункт є продовженням попереднього. Через проблеми зі швидкістю існує ризик, що елемент, який зʼявляється на невеликий проміжок часу, не буде знайдений вчасно. Наприклад, Appium може не встигнути відловити Snackbar (toast), що призведе до падіння тесту.

Нерозпарсені елементи

Приклад з мого досвіду: в застосунку змінилася логіка, і на екран додали більше елементів. Якщо раніше тести проходили успішно, то після цих змін Appium не міг розпарсити певні елементи. Замість того, щоб повернути XCUICell з його атрибутами, нам поверталося XCUIElementTypeAny. Жодних рішень, окрім зменшення кількості елементів на екрані, ми не знайшли.

Стабільність

Тести на Appium менш стабільні. Буває, що ви бачите елемент на екрані (умовно, у звіті є скриншот), проте Appium його не знаходить. Після повторного запуску тесту все працює. З нативним тестуванням такі випадки трапляються значно рідше.

Налаштування та залежність від оновлень Appium

Іноді під час роботи з Appium ваші тести можуть не запускатися, а в колег усе буде в порядку. Часто це трапляється через проблему версійності. Також після оновлення Appium, можливо, доведеться переписувати значну частину коду. Це подібно до переходу з однієї версії мови програмування на іншу, але тут ви оновлюєте бібліотеку, щоб отримати нову функціональність або підтримку нових можливостей. З оновленням Appium Server потрібно вручну оновлювати всі машини, на яких ви його запускаєте.

Підтримка нових функцій і виправлення помилок в Appium є. Проте немає жодних гарантій, що його не припинять підтримувати. Наприклад, про баг з XCUIElementTypeAny писали 6–12 місяців тому, і за цей час нічого не змінилося.

Переваги нативного тестування

Швидкість виконання тестів

Це одна з найголовніших переваг нативного тестування. Якщо його налагоджено правильно, це забезпечує високу швидкість виконання тестів.

Актуальна версія мови та фіч

Коли ми пишемо тести в тому ж проєкті, то завжди працюємо з актуальною версією, яку використовують у розробці. Це унеможливлює ситуації, коли розробники використовують нові елементи з останньої версії Swift, а тести не вміють з ними взаємодіяти.

Участь розробників у підтримці тестів

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

Цей аспект також корисний для тих, хто тільки починає шлях в автоматизації: розробники можуть надавати поради та проводити рев’ю ваших тестів. Також ви будете «варитися» в застосунку й глибше розуміти процеси розробки.

На одному з проєктів моїм завданням було впровадити автоматизацію тестування з нуля. Після аналізу вимог запропонував два варіанти: Appium та нативні тести. Я реалізував один і той самий тест-кейс у двох варіантах та презентував замовнику, описавши плюси Appium. Але зрештою він обрав нативні тести.

Коли база була готова і тести вже бігали на CI/CD, до процесу написання почали долучати розробників. Перші End-to-End та UI-тести були неідеальними, адже написані прямолінійно й без будь-яких корнер-кейсів. Але за кілька місяців все налагодилося, і покриття застосунку тестами виросло. Через війну, на жаль, замовник закрив співпрацю з командами в Україні, але розробники й надалі зможуть підтримувати покриття тестами.

Ідентифікатори

Під час написання нативних тестів ви можете самостійно додавати ID до необхідних елементів, не чекаючи, поки розробник звільниться. У випадку з iOS та Android (з декларативним підходом) зможете додати accessibilityIdentifier/TestTag. Хоча це потребує трохи більше (а інколи і суттєво більше) часу, але це явний плюс. Хоча, якщо ви знаєте, умовно, Swift і у вас є доступ до репозиторія, ви можете також їх додати. Але у випадку з нативками це виглядає більш органічно. При роботі на Android з імперативним підходом усі ID вже є за замовчуванням.

Налаштування застосунку перед його запуском

З нативними тестами ви можете передавати конфігурації для запуску застосунку. Це дає змогу не проходити через UI щоразу, а налаштувати параметри заздалегідь. Також можна передавати не лише аргументи, а й повноцінні ключі-значення. Наприклад, можна мокати дані в такий спосіб або конфігурувати застосунок відповідно до вимог. В Appium є подібна функціональність, проте працює вона значно складніше, якщо порівнювати з нативним тестуванням. Також додавання можливості прокидувати змінні через Appium для Android має потенційні вразливості.

UI-тестування

Нативне тестування допомагає легко працювати з UI, перевіряючи, чи працює екран або функціонал коректно. Це заощаджує час, адже не потрібно проходити повний флоу застосунку. До того ж це відкриває можливості для Snapshot-тестування, оскільки ви залежите лише від замоканих даних, а не від того, що надає бекенд. Кейси з UI-тестуванням в Android працюють «з коробки», в iOS потрібно трохи змінити код.

Виправлення багів

Автоматизатори можуть виправляти баги та дебажити застосунок, якщо виникає неочікувана поведінка. Це велика перевага, адже зберігає час на виявлення проблеми.

Кейс із практики: на одному з проєктів тести були нестабільними, оскільки в застосунку існувала певна логіка очікування даних з бекенду, які не завжди приходили вчасно. Це не сильно турбувало бізнес, адже користувачі могли просто перевідкрити екран. Для автотестів це не дуже гарний підхід. Я завів цей баг, але розробники довгий час не бралися за нього. Одного прекрасного дня я просто сів, розібрався, як працює цей функціонал, і виправив його. В результаті всі лишилися задоволеними, а мої тести стали стабільнішими та займали менше часу на перезапуск.

Недоліки нативного тестування

Необхідність знання мови програмування застосунку

Під час нативного тестування ви маєте знати мову (мови), якою написаний застосунок: Swift (рідше Objective-C) для iOS та Kotlin (рідше Java) для Android-проєкту. Ще можна виділити, що складніше шукати фахівців, оскільки знайти людину зі знанням Java зазвичай легше, ніж зі знаннями Swift та/або Kotlin.

Також ви обмежені можливостями обраної мови й тим, що використовують розробники у застосунку (залежності, бібліотеки тощо). Щоразу, коли ви запускаєте тест, застосунок має білдитися. Хоча цей процес зазвичай швидкий, необхідність виконувати його щоразу може стати перепоною. Якщо ви, наприклад, використовуєте Java разом з Appium, то можете просто додати скрипт до основної функції й викликати його. Або створити тест, який запуститься перед основним тестуванням. Це займе значно менше часу, оскільки білд тестів зазвичай швидший, ніж збірка всього проєкту з застосунком.

Проте, якщо ви знаєте Swift і Kotlin, вам буде нескладно написати скрипт на Kotlin (або іншою мовою програмування) і запустити його перед iOS-тестами. Наприклад, перед запуском наших тестів я запускаю скрипт на Kotlin, який створює ран у TestRail і виконує деякі налаштування тестового середовища.

iOS Reports

На сьогодні існує обмаль зручних рішень для репортингу на iOS. Стандартний XCResult є громіздким і не дуже зручним. Логи в ньому часто виключно технічні та суттєво відрізняються від того, до чого звикли більшість тестувальників. Для перегляду звіту потрібно відкривати Xcode. Розмір файлу може бути досить великим, а його потрібно завантажувати щоразу після рану.

Альтернативою є open-source проєкт xchtmlreport, який вирішує проблему вивантаження файлу кожним учасником процесу. Проте він все одно вимагає стандартного репорт-файлу. Крім того, логування лишається таким самим, як у XCResult, просто в HTML-форматі.

Приклад стандартного репорта в iOS:

Щоби вирішити цю проблему, ми створили власний логер у проєкті, який збирає та записує всю інформацію у JSON. Кроки тестів описуються в функціях, падіння перехоплюються та логуються. Далі за допомогою нашої утиліти всі логи перетворюються на HTML-сторінки та публікуються на нашому сервісі, до якого мають доступ усі учасники через браузер. Це рішення працює на будь-якій платформі та мові. За потреби ми можемо оперативно щось додати або змінити.

Приклад нашого репорту (можливо, не дуже привабливий дизайн, але в нього інша ціль 🙂):

Пошук елементів

В iOS-застосунках для пошуку елементів існує щось схоже на XPath. Хоча для батьківських елементів може знадобитися написання додаткового коду, це не викликає значних труднощів. З Android ситуація складніша: потрібно докласти зусиль, щоби знайти елемент не лише за його ID або TestTag, але й, наприклад, за дочірнім елементом або за індексом.

Одним із рішень є написання розширень для класу або створення кількох функцій, які можна використовувати в подальшій роботі. Це рекомендується робити на початку, коли ви налаштовуєте фреймворк або пишете перші тести. У такий спосіб можна заощадити час та спростити роботу новим членам команди. Звісно, можуть траплятися елементи, які потрібно шукати в нестандартний спосіб, але приблизно 90 % випадків ця функціональність покриє.

Приклади коду:

iOS (Swift):

// Перша кнопка в застосунку
let firstButton = app.buttons.firstMatch
// Текстовий елемент з індексом 1
let secondText = app.staticTexts[1]
// Елемент з accessibilityIdentifier
let element = app.otherElements["element_id"]
// Кнопка в середині іншого cell елемента
let buttonInAnotherElement = app.cells["cell_id"].buttons["common_button"]
// Кнопка з певним текстом
app.buttons.matching(NSPredicate(format: "label CONTAINS %@", "text"))

Android Espresso (Kotlin):

// Кнопка з ID
val loginButton: ViewInteractionbylazy { onView(withId(R.id.login_button)) }
// Текст в середині іншого елемента
val emailError: ViewInteraction by lazy {
	onView(
		allOf(
			isDescendantOfA(withId(R.id.username_container)),
			withId(R.id.textinput_error)
		)
	)
}

// Функція яка шукає елемент за індексом
fun elementFromMatchAtPositionForEspresso(matcher: Matcher<View>, position: Int): Matcher<View> {
   return object : BaseMatcher<View>() {
       var counter = 0
       override fun matches(item: Any): Boolean {
           if (matcher.matches(item)) {
               if (counter == position) {
                   counter++
                   return true
               }
               counter++
           }
           return false
       }

       override fun describeTo(description: Description) {
           description.appendText("Element at hierarchy position $position")
       }
   }
}

Імперативний та декларативний підходи в Android

Android-застосунок може бути написаний як імперативним, так і декларативним підходом. А іноді — змішаним, наприклад, коли додають функціональність, використовуючи новий підхід. У таких випадках тестування також доведеться адаптувати.

Для цього можна створити універсальні екстеншени до класів. Наприклад, обернути perform(click()) у власну функцію tap(), тоді під час написання тестів не буде різниці, чи використовується Espresso, чи Jetpack Compose Testing. Функції матимуть однаковий вигляд, і це спрощує підтримку тестів. Рекомендую впроваджувати такі абстракції на початкових етапах створення тестового фреймворку. Це не лише зменшує обсяг коду, але й значно полегшує переписування екрана з одного підходу на інший. Тоді вам потрібно буде лише замінити елементи в класі, який описує екран (у разі використання Page Object Pattern).

Приклади

Розширення для базових функцій. Espresso:

val ViewInteraction.isDisplayed: Boolean
    get() = getStatusForMatcher(ViewMatchers.isDisplayed())

val ViewInteraction.isEnabled: Boolean
    get() = getStatusForMatcher(ViewMatchers.isEnabled())

fun ViewInteraction.tap(): ViewInteraction {
    scrollToElement()

    return perform(ViewActions.click())
}

Jetpack Compose:

val SemanticsNodeInteraction.isDisplayed: Boolean
    get() =
        try {
            assertIsDisplayed()
            true
        } catch (ignore: Throwable) {
            false
        }

val SemanticsNodeInteraction.isEnabled: Boolean
    get() = getStatusForMatcher(isEnabled())

fun SemanticsNodeInteraction.tap(): SemanticsNodeInteraction {
    scrollToElement()

    return performClick()
}

Ініціалізація елементів (приклад з двох різних класів):

// Espresso елемент
val saveButton by lazy { onView(withId(R.id.save)) }

// Jetpack Compose testing елемент
val doNotShowButton by lazy { composeTestRule.onNodeWithTag(CalendarTestTags.educationDontShowButton) }

Екран з Espresso-елементами:

func clickSaveButton() {
	logger.log("Click 'Save' button")
	saveButton.tap()
}

Екран з Jetpack Compose елементами:

func doNotShowButton() {
	logger.log("Click 'Do not show' button")
	doNotShowButton.tap()
}

Як бачите з прикладів вище, під час використання елементів немає жодної різниці, за яким підходом був написаний екран. Це важливо лише для ініціалізації елементів, для якої можна написати окремий екстеншен або утилітарну функцію. Ваші тести та інша логіка матимуть однаковий вигляд, що допоможе не витрачати час на визначення типу цього елемента. Інкапсуляція в дії 🙂

Банальні дії не такі й банальні

Деякі, здавалося б, прості речі мають йти «з коробки», але не в Android, принаймні в конкретному кейсі. Можливо, за останній час щось змінилося, але декілька років тому я стикнувся з тим, що не зміг отримати текст з елемента на екрані. Як завжди, рішення є — просто додай трішки коду. На допомогу знову приходять екстеншени для класів-елементів.

Espresso:

val ViewInteraction.text: String
    get() {
        var result = EMPTY_STRING
        try {
            perform(object : ViewAction {
                override fun getConstraints(): Matcher<View> {
                    return isAssignableFrom(TextView::class.java)
                }

                override fun getDescription(): String {
                    return "getting text from a TextView"
                }

                override fun perform(uiController: UiController?, view: View) {
                    val tv = view as TextView //Save, because of check in getConstraints()
                    result = tv.text.toString()
                }
            })
        } catch (ignoreIfElementNotFound: NoMatchingViewException) {
        }

        return result
    }

Jetpack Compose:

val SemanticsNodeInteraction.text: String
    get() {
        return try {
            val textLayoutResults = mutableListOf<TextLayoutResult>()
            performSemanticsAction(SemanticsActions.GetTextLayoutResult) { it(textLayoutResults) }

            textLayoutResults[0].layoutInput.text.text
        } catch (ignore: Throwable) {
            EMPTY_STRING
        }
    }

Це дає змогу діставати текст з будь-якого елемента без проблем і зайвого коду: написали на початку проєкту і забули.

То що ж обрати

Вибір залежить від вашої експертизи та команди, а також застосунку, його складності й концепцій тестування.

Найпростіший шлях — працювати з Appium, але варто враховувати його недоліки: повільність та потенційні вразливості. Для роботи з простим застосунком вони будуть менш відчутними. Якщо ж він сильно виросте і стане навантаженим елементами, це може бути критичним для Appium.

Якщо ви працюєте з доволі великим або навантаженим елементами застосунком, вам принципова швидкість рану тестів або додатково потрібні UI-тести, варто дивитися в бік нативного тестування. Хоча я описав певні недоліки цього підходу, їх так чи інакше можна обійти. Чого не зробиш з Appium, який не зміг розпарсити 20 однакових елементів і повертав XCUIElementTypeAny.

Нативне тестування — складніший підхід. Але, якщо в ньому розібратися, це дає свої плоди: швидкість та можливість впливати на його поведінку зсередини. Для мене нативне тестування й Appium — це як iPhone та Meizu. Функціонально схожі, обидва можуть дзвонити, але різниця є. На мій погляд, цей вид тестування — більш прогресивний, і я радий, що в моїй компанії віддають йому перевагу.

Якщо спочатку є сумніви, що обрати, я б рекомендував зробити Proof of Concept з двома підходами для кількох тестів. До нього варто включити складний тест, де буде багато екранів та елементів, і за результатами робити висновки, який підхід кращий для вашого проєкту.

На завершення декілька прикладів, які показують: якщо все правильно налаштувати, то візуально різниця між підходами не сильно помітна. Якщо це так, то вибирайте нативне те, що вважаєте за потрібне 🙂

iOS (Swift):

  func testSignInWithEmail() {
        let isLoginScreenPresent = startScreen.openLoginScreen()
            .continueWithEmail()
            .loginBy(user: RealUser.defaultUser)
        
        assertFalse(condition: isLoginScreenPresent, failureMessage: "User is not logged on or 'Login' screen still present")
    }

Android (Kotlin + Native Espresso/Jetpack Compose):

@Test
fun testSignInWithEmail() {
	val profileScreen = startScreen.openLoginScreen()
            	.continueWithEmail()
            	.loginByUser(RealUser.defaultUser)

	Assert.assertFalse(profileScreen.successLogin.exists, "User is not logged on")
}

Q&A

Перед публікацією цієї статті я попросив Head of QA з партнерської компані Genesis дати фідбек, і отримав декілька цікавих питань. Вирішив винести їх окремим розділом і відповісти на них тут.

— Чи можливий гібридний підхід Native + Appium? Наприклад, Native віддавати розробникам для компонентного покриття, а юзкейси покривати з Appium.

Це цілком робочий варіант, коли розробники пишуть окремо UI та інші тести, а вже тестувальники займаються End-to-End сценаріями. Особисто я обирав би нативки в цьому випадку, щоб далі розвивати фреймворк і перевикористовувати спільні речі (і загалом в мене був саме такий проєкт, коли деви робили Snapshot-тестування, а QA почали Е2Е і ми обʼєднали зусилля + додали UI-тести).

Утім, на мою думку, це не дуже доречно. Оскільки ми зав’язуємо тестування на розробників, Е2Е-перевірки можуть дублювати UI-компонентні, а це зайва робота/час на прогін.

Тому, як на мене, у проєктах, де є або впроваджується автоматизація, розділяти не варто.

— Чи потрібно стандартизувати репортинг у Native-автоматизації різними мовами?

Не обовʼязково, але, як на мене, бажано. Коли ми почнемо покривати Android, зможемо використовувати той самий репорт, оскільки він буде однаковим для обох платформ. Також він має додаткові переваги, які плануємо використовувати, зокрема, для Android.

— Коли тестам потрібні прекондішени у вигляді створених даних та налаштувань у бекенді продукту, як це реалізувати в Native-автоматизації? Чи потрібно робити окремі реалізації API клієнтів на кожну мову?

У випадку з будь-якими API-запитами це потрібно виконувати через реквести до бекенду. У такій ситуації нам необхідно мати клієнти для обох платформ або розробити окремий скрипт, який запускатиметься перед тестами та генеруватиме всі необхідні дані. Теоретично, якщо запити складні й важко підтримувати їхню логіку, можна створити сервіс-прошарок, на який надсилатимуться прості запити, а він уже виконуватиме потрібний ланцюг викликів. Таким чином, підтримувати доводилося лише один сервіс, до якого зверталися.

— Чи є відмінності у підходах з точки зору дефектів, які можна пропустити?

Як я описував вище, у випадку використання Appium є ризик, що ми не зможемо отримати елемент вчасно. Отже, нам невідомо, чи правильно відпрацьовує застосунок, і це потрібно перевіряти «руками».

— Чи впливають інтеграційні та UNIT-тести на вибір підходу в E2E-автоматизації або її кількості?

З мого досвіду — ні. На жаль, проєктів з високим рівнем покриття UNIT-тестами дуже мало (мені такі майже не траплялися). У будь-якому разі наявність повного покриття чи його абсолютна відсутність зазвичай не впливають на вибір технологій для покриття функціоналу E2E-тестами. Якщо на проєкті є інтеграційні тести, в окремих випадках можна уникнути взаємодії з бекендом, створюючи частково UI-тести з використанням mock-даних. Однак і тут ми знову повертаємося до нативного тестування.

Для Appium можна використати мок-сервери, але це затратніше, ніж умовний мок з файлу — і інфраструктурно, і в плані стабільності та часу.

Також зазначу, що отримати чітку документацію або розуміння того, що саме покрито UNIT-тестами, доволі складно. Через це важко вирішити, де можна пропустити написання E2E-тестів для певного функціоналу.

— Тестування на Real Mobile Devices можливе для обох підходів? Чи є якісь особливості?

Як на мене, різниці немає, хіба що паралелізацію під iOS простіше зробити з Appium, ніж з Native.

👍ПодобаєтьсяСподобалось14
До обраногоВ обраному11
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

Дякую за досвід! Ми також проходили цей шлях. Обрали нативне тестування і для mobile ios, і для Android.
Appium лишили лише для desktop windows i linux.

Трохи дивно, бо згідно схеми «потрібно мокати дані» веде до Native. Чому?

В статі про це писав, що в Appium можна мокати дані, але це дорожче. Для Android це може бути не секʼюрно. Наприклад у випадку, коли треба відкрити певний екран з певними даними.
В нативній реалізації це простіше і гармонічніше + якщо змінилася модель даних, ви можете використовувати модель яку юзають розробники і тим самим спростити цей процес.

П.С. Я не кажу, що цього зробити не можливо, я більш про те, що варіант з нативками більш простий, гнучкий і секʼюрний в цьому плані.

Офігенна стаття, вичерпна та дуже файно описує біль який є в мобілках
Любив той час, стільки складнощів, лазити в іос та андроід апки які мігрують з версії на версію додввти свої айдішки туди, дані, а виявилось ми ще й аксесебіліті рфігенне зробили для юзерів,афігенно

Цікаво думка автора щодо тестування кросплатформених застосунків на React Native чи інших аналогах. Там по суті нативний код мінімальний.

Гібридний підхід і апіум і плейрат може зайти. Мав такий кейс

React Native

Стосовно додатку на React Native я, якщо чесно, не працював багато з таким. В нас частково він використовувався, і з ним було геморно працювати з нативних тестів, бо, якщо я правильно все памʼятаю, було навіть важко туди додати ID і всі елементи шукалися виключно за текстом. І цей кусок додатку менеджився іншою командою.

Якщо брати повний додаток на React Native, то я точно не підкажу, але я б почави би вивчати це питання. Якщо я не помиляюся, то на рекат є свої нативки. Також глянув би на Detox, він, здається як раз для React Native. Ну а далі вже від того що отримав би на виході ресьорчу.
В теорії тут можна і нативки (ios/android) використовувати, шукаючи всі елементи по тексту, але, скоріш за все це буде зайве. Ну і звісно як одна із опцій це Appium :)

Або, як написав Роман можна було б спробувати плейрайт. Але це вже залежитиме від проєкту, цілей, що в нас є і тд.

При нативном тестировании есть основная проблема: если аппликуха должна поддерживаться в iOs и Android, то в процессе разработки можно всё писать для 1 операционки, затем в 2 клика транслировать код для использования в другой.
Не уверен, что так же можно поступить с тестами. Впрочем, никогда не проверял, могу оказаться неправ.

Appium тесты можно использовать с минимальными перенастройками, в основном касающимися локаторов (в некоторых местах, где UI отличается из-за разных компонентов, которые юзают iOs и Android). А вот можно ли код тестов с 1 языка перенести на 2-й без полного его переписания и гемора, связанного с поддержкой зоопарка в 2-х репозиториях — вот тут у меня есть сомнения...

Думаю навряд. Як Ви і казали, локатори + UI будуть відрізнятися. Якщо взяти якийсь Flutter то в теорії може і так. Додаток і тести однією і тією ж кросплатформною мовою. Але я такого не перевіряв)

я от все чекаю, коли зʼявиться на ринку мобілок рішення накшталт плейрайта для вебу — швидке, просте, зручне :)
бо апіум — це біль болюча. більше клопотів, ніж вихлопу.
а стаття чудова! автор молодець!

Дякую. А от щось по типу плейрайта було б супер, але коли? :)

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