Stub: що це, види та застосування в тестуванні

Автор: IT Sectr Опубліковано: 2026-04-10 Час читання: 9 хв

Stub (стаб, заглушка) — тестовий об’єкт, що повертає попередньо визначені відповіді на виклики методів замість реальної реалізації. У мобільній розробці стаби ізолюють тестований модуль від мережевих запитів, бази даних та файлової системи, дозволяючи перевіряти логіку без налаштування середовища. На відміну від mock, stub не перевіряє поведінку — він лише надає дані. Докладніше в статті Martin Fowler про test doubles.

Головне

  • Stub — заглушка, яка повертає задані значення на виклики методів без логіки
  • Ізоляція — стаби вимикають реальні залежності: API, БД, файли, сенсори
  • Відмінність від Mock — stub не верифікує виклики, він лише підміняє відповідь
  • Android — MockWebServer (OkHttp) як stub для HTTP, MockK.constantAnswer для Kotlin
  • iOS — OCMock та протоколи Swift з тестовими реалізаціями як стаби

Що таке Stub та чим він відрізняється від інших test doubles?

Stub — це об’єкт-заглушка, який замінює реальну залежність в тесті та повертає зараніше визначені значення на певні виклики. Термін був введений в класифікації Gerard Meszaros (2007) у книзі «xUnit Test Patterns». Stub належить до категорії test doubles — об’єктів, що підмінюють реальні компоненти під час тестування. Основна мета стаба — забезпечити тестований блок предикабльними даними, прибравши невизначеність зовнішніх систем.

Принцип роботи — тест конфігурує стаб перед виконанням: «коли метод getUsers() буде викликаний, поверни цей список користувачів». Stub не містить бізнес-логіки, не перевіряє послідовність викликів та не записує історію звернень. Він просто стоїть на місці реального компонента і повертає те, що йому сказали. В контексті Android-тестування це означає: OkHttp-клієнт не робить реальний запит на сервер, а отримує відповідь з MockWebServer, налаштованого як stub.

  • Stub — повертає дані, не перевіряє виклики
  • Mock — повертає дані та перевіряє поведінку (verify)
  • Fake — робоча спрощена реалізація з справжньою логікою
  • Spy — обгортає реальний об’єкт, записуючи виклики
  • Dummy — передається, але не використовується (null, порожній об’єкт)

Коли використовувати — стаби оптимальні для тестування UI-шару (ViewModel, Presenter) та бізнес-логіки (UseCase, Interactor), де потрібно перевірити реакцію на конкретні дані: список порожній, сервер повернув помилку 500, строк токена закінчився. Будь-який кейс, де тест вимагає певного вхідного стану — це завдання для stub. На кожен тестовий сценарій створюється своя конфігурація стаба, що робить тести читанними та предикабльними.

Класифікація test doubles за Meszaros

Gerard Meszaros (2007) у книзі «xUnit Test Patterns» виділив п’ять типів test doubles: dummy, stub, spy, mock, fake. Кожен тип вирішує своє завдання. Dummy — передається, але не використовується. Stub — повертає дані. Spy — записує виклики. Mock — перевіряє поведінку. Fake — містить спрощену логіку. Розуміння цієї класифікації допомагає розробнику вибирати правильний інструмент для кожного тестового сценарію.

Де використовуються стаби в тестуванні мобільних застосунків

Стаби для мережевих запитів

Мережеві запити — найчастіший сценарій застосування стабів. Застосунок робить HTTP-виклики до API, і в тесті потрібно перевірити реакцію на різні відповіді: успішний JSON, помилка 401 (неавторизований), таймаут, порожній масив. MockWebServer (OkHttp) на Android та URLProtocol (iOS) виступають як стаби, повертаючи попередньо визначені HTTP-відповіді без реального з’єднання з сервером. Це прискорює тести з секунд до мілісекунд.

База даних — Room (Android) та CoreData (iOS) мають ін-меморі варіанти, але їх налаштування все одно потребує часу. Stub замість репозиторію повертає зараніше підготовлені списки Entity, не торкаючись БД. Це особливо ефективно для тестування ViewModel, де потрібно перевірити сортування, фільтрацію або трансформацію даних. Тест виконується за мілісекунди незалежно від обсягу даних.

Системні сервіси — LocationManager, SensorManager, SharedPreferences потребують реального пристрою або емулятора. Stub для LocationProvider повертає задані координати, для SensorManager — фіксовані значення акселерометра. На iOS аналог — CLLocationManager з тестовою реалізацією делегата. Без стабів такі тести вимагають фізичного пристрою з певними умовами.

Файлова система та кеш — завантаження зображень, кешування відповідей, робота з файлами конфігурації — всі ці операції залежать від стану диска. Stub для FileManager або ImageCache повертає успіх/помилку без читання реальних файлів. Це усунає хибні падіння тестів через незбіг шляхів або прав доступу на різних машинах розробників.

Stub vs Mock vs Fake: ключові відмінності

Розподіл відповідальності — три типи test doubles вирішують різні завдання. Stub: «дай мені дані». Mock: «перевір що мене викликали». Fake: «я працюю як справжній, тільки простіше». Різниця критично важлива для читанності тестів: якщо тест використовує mock там, де потрібен stub, він перевантажений verify-викликами, що не стосуються сценарію, що перевіряється.

ХарактеристикаStubMockFake
ПризначенняНадати даніПеревірити взаємодіюСпрощена реалізація
ЛогікаНемаєНемаєЄ (але спрощена)
ВерифікаціяНемаєТак (verify)Непряма (через стан)
ГнучкістьНизька — жорсткі відповідіСередняВисока — логіка адаптується
ШвидкістьМаксимальнаВисокаСередня
ПрикладMockWebServer повертає JSONMockito.verify(repository).save()InMemoryRepository з HashMap

Практичне правило — якщо тест перевіряє, які дані отримав тестований компонент — використовуйте stub. Якщо тест перевіряє, чи викликав компонент метод залежності з правильними аргументами — використовуйте mock. Якщо ви просто хочете замінити БД на хеш-таблицю — це fake. Змішування типів в одному тесті робить його хрупким: при зміні реалізації доведеться переписати і stub, і verify-логіку.

Антипатерн: Stub з verify

Stub з verify — поширена помилка, коли розробник налаштовує stub, а потім додає verify(stub).method(). Stub за визначенням не повинен верифікуватися — для верифікації є mock. Якщо вам потрібно перевірити, що метод був викликаний з конкретними аргументами, використовуйте Mockito.mock() замість Mockito.stub(). Цей поділ зберігає намір тесту зрозумілим для інших розробників.

Реалізація стабів на Android з MockWebServer та MockK

MockWebServer — бібліотека OkHttp для створення HTTP-стабів на Android та JVM. Вона запускає локальний HTTP-сервер на вказаному порті, який перехоплює запити OkHttp-клієнта та повертає попередньо визначені відповіді. Налаштування займає три рядки: створити сервер, додати відповідь у чергу, запустити його. Тест може послідовно додавати в чергу кілька відповідей для сценаріїв з пагінацією або повторними спробами.

kotlin
class UserRepositoryTest {

    private val server = MockWebServer()

    fun setup() {
        server.start(8080)
        val client = OkHttpClient.Builder()
            .readTimeout(1, TimeUnit.SECONDS)
            .build()
    }

    fun test_user_list_success() {
        val json = "[{\"id\":1,\"name\":\"Alice\"}]"
        server.enqueue(MockResponse()
            .setBody(json)
            .setResponseCode(200)
        )
        val result = repository.getUsers()
        assertEquals(1, result.size)
    }

    fun teardown() {
        server.shutdown()
    }
}

MockK — альтернатива Mockito для Kotlin з підтримкою першого класу для корутин, функцій розширення та sealed-класів. Стаби в MockK створюються через coEvery (для suspend-функцій) та every (для звичайних). На відміну від MockWebServer, MockK стабить окремі методи-залежності, а не весь HTTP-шар. Це зручно для модульних тестів UseCase або Interactor, де залежності — це абстракції репозиторіїв.

kotlin
interface UserRepository {
    suspend fun getUsers(): List<User>
}

class GetUsersUseCaseTest {

    private val repo = mockk<UserRepository>()

    private val useCase = GetUsersUseCase(repo)

    fun test_empty_list() = runTest {
        coEvery { repo.getUsers() } returns emptyList()

        val result = useCase.invoke()

        assertTrue(result.isEmpty())
        coVerify(exactly = 1) { repo.getUsers() }
    }
}

Best practice — для інтеграційних тестів використовуйте MockWebServer (перехоплює реальний HTTP), для модульних тестів — MockK (стабить інтерфейси). Не стабте те, що ви не тестуєте: якщо тест перевіряє Repository, не стабте OkHttp-клієнт всередині нього — використовуйте справжній MockWebServer на рівні HTTP. Це правило зберігає тести релевантними та зменшує хрупкість під час рефакторингу.

Реалізація стабів на iOS з OCMock та протоколами

Swift-протоколи як стаби — в iOS-нативному підході stub реалізується через підстановку тестової структури, що відповідає протоколу залежності. Замість справжнього NetworkService тест отримує StubNetworkService, який повертає фіксовані дані. Swift — мова зі статичною типізацією, тому stub повинен відповідати тому ж протоколу, що й справжній сервіс. Компілятор гарантує, що stub реалізує всі необхідні методи.

swift
protocol NetworkServiceProtocol {
    func fetchUsers() async throws -> [User]
}

struct StubNetworkService: NetworkServiceProtocol {
    let result: Result<[User], Error>

    func fetchUsers() async throws -> [User] {
        try result.get()
    }
}

final class UsersViewModelTests: XCTestCase {
    func test_success_state() async {
        let stub = StubNetworkService(
            result: .success([User(name: "Alice")])
        )
        let vm = UsersViewModel(service: stub)
        await vm.load()
        XCTAssertEqual(vm.users.count, 1)
    }
}

OCMock для Objective-C — бібліотека для створення стабів та моків у застарілих iOS-проектах. OCMock підтримує stub-методи з аргументами та поверненими значеннями. Сучасні проекти на Swift надають перевагу підходу на основі протоколів з ручними стабами — це дає контроль над кожним методом і не потребує зовнішніх залежностей. OCMock залишається опцією для проектів, де протоколізація всіх залежностей економічно недоцільна.

URLProtocol для HTTP-стабів — системний механізм iOS для перехоплення мережевих запитів через підклас URLProtocol. Тест реєструє кастомний URLProtocol, який перехоплює URLSession та повертає stub-відповіді. Перевага перед ручними стабами: не потрібно змінювати архітектуру застосунка — URLSession залишається справжнім, але дані підмінюються на рівні протоколу. Недолік: складніше налагоджувати, ніж явний stub-сервіс.

Часто запитують

Чим Stub відрізняється від Mock?

Stub повертає попередньо визначені дані та не перевіряє факт виклику. Mock додатково верифікує, що метод був викликаний з правильними аргументами (verify). Stub відповідає на питання «що повернути», Mock — на питання «чи був виклик». Використовуйте stub для перевірки стану, mock — для перевірки взаємодії.

Коли використовувати Fake замість Stub?

Fake потрібен, коли тесту потрібна працююча (навіть спрощена) реалізація — наприклад, ін-меморі база даних замість Room. Stub підходить для одиничних сценаріїв з попередньо визначеними даними. Якщо ви повторюєте одного й того ж stuba в 10 тестах — наймовірніше, вам потрібен Fake. Fake зменшує дублювання, оскільки логіка живе в одному класі.

Чи можна стабити статичні методи?

На Android — MockK для Kotlin-об’єктів (object) підтримує mockkObject(), включаючи статичні методи Java-класів через mockkStatic(). На iOS — статичні методи Swift не стабяться безпосередньо; використовуйте протоколи та DI, щоб замінити static-виклик на інстансний метод протоколу. Статичні стаби — технічний борг, їх слід уникати в новому коді.

Як стабити мережеві запити на Android?

Використовуйте MockWebServer (OkHttp) — він працює як локальний HTTP-сервер, що додає відповіді у чергу. Для Retrofit достатьньо замінити базову URL на localhost:8080. Для Ktor використовуйте MockEngine — вбудований механізм для підміни HttpStatement. Обидва підходи працюють без реального інтернету та дають повний контроль над кодом статусу, тілом та заголовками відповіді.

Stub vs Spy — в чому різниця?

Spy обгортає реальний об’єкт та записує виклики, а Stub повністю замінює об’єкт фіксованими відповідями. Spy дозволяє частково використовувати реальну реалізацію (інші методи працюють як є), а stub — ні. Якщо вам потрібно перевірити, що метод був викликаний, але частина логіки має виконатися — використовуйте spy, а не stub.

Підсумки

  • Stub — об’єкт-заглушка, що повертає попередньо визначені відповіді на виклики методів під час тестування
  • Ізоляція залежностей — стаби замінюють мережеві запити, БД, системні сервіси та файлову систему
  • Відмінність від Mock — stub не верифікує виклики, він лише повертає дані без перевірки поведінки
  • Android-інструменти — MockWebServer для HTTP, MockK для Kotlin-інтерфейсів з підтримкою корутин
  • iOS-інструменти — протокольні стаби на Swift, URLProtocol для HTTP, OCMock для Objective-C
  • Не змішуйте ролі — не додавайте verify до stub, використовуйте mock для верифікації викликів
  • Stub + MockWebServer — стандартний підхід для інтеграційних тестів без реального сервера

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також