Fake — що це, призначення та як використовувати в тестуванні

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

Fake (фейк) — робоча спрощена реалізація залежності, яка поводиться як справжній компонент, але використовує in-memory сховище або інші легкі механізми замість продакшен-інфраструктури. На відміну від stub, fake містить реальну бізнес-логіку — сортування, фільтрацію, агрегацію — просто без зовнішніх ефектів. In-memory база даних замість Room або HashMap замість SharedPreferences — класичні приклади. Докладніше — в класифікації test doubles Мартіна Фаулера.

Головне

  • Fake — спрощена робоча реалізація з реальною логікою, але без зовнішніх залежностей
  • In-memory зберігання — fake-репозиторій зберігає дані в HashMap, а не в БД
  • Відмінність від Stub — stub повертає фіксовані дані, fake містить виконувану логіку
  • Android — InMemoryUserRepository як Fake для тестування ViewModel і UseCase
  • iOS — FakeNetworkSession із URLProtocol і тестовими даними замість реального сервера

Що таке Fake і навіщо він потрібен у тестуванні?

Fake — це повноцінна, але полегшена реалізація інтерфейсу, придатна для тестування. Термін введений Gerard Meszaros (2007) у книзі «xUnit Test Patterns.» На відміну від stub, який повертає жорстко задані відповіді, fake містить виконуваний код: він може сортувати список, фільтрувати за умовою, рахувати кількість записів. Єдина відмінність від продакшен-реалізації — fake працює з in-memory даними і не виконує реальних I/O-операцій.

Основна перевага — швидкість. Тести з fake виконуються за мілісекунди, тому що немає звернення до диска, мережі або бази даних. In-memory HashMap працює в 100—1000 разів швидше за Room або CoreData. При цьому fake перевіряє реальну бізнес-логіку: сортування, фільтрацію, агрегацію — все те, що stub не може перевірити, тому що stub повертає тільки те, що йому сказали. Fake дає впевненість, що код правильно обробляє дані, а не просто отримує попередньо визначену відповідь.

Коли Fake кращий за Stub

Fake кращий за Stub — якщо тестований компонент виконує кілька операцій над даними (дістав, відфільтрував, відсортував, зберіг), stub потребуватиме налаштування кожного виклику окремо. Fake містить логіку всередині себе — тест просто викликає методи і перевіряє результат. В IT Sectr ми використовуємо fake для всіх репозиторіїв в unit-тестах: fake-репозиторій із HashMap покриває 90% сценаріїв без налаштування Mockito або MockK.

Fake vs Stub vs Mock: коли що вибирати

Критерій вибору — визначте, що перевіряє тест: стан чи взаємодію. Якщо тест перевіряє стан (результат роботи) і використовує логіку — потрібен fake. Якщо тесту потрібні тільки вхідні дані без логіки — достатньо stub. Якщо тест перевіряє факт виклику методу — потрібен mock. Змішування типів test doubles в одному тесті ускладнює розуміння і збільшує крихкість.

КритерійFakeStubMock
Наявність логікиТак (спрощена)НіНі
ШвидкістьВисокаМаксимальнаВисока
Перевірка поведінкиНепрямаНіТак (verify)
ОбслуговуванняОдин клас на інтерфейсНалаштування під кожен тестНалаштування під кожен тест
РеалізмВисокий (код працює)Низький (жорсткі дані)Середній
Ризик хибних спрацьовуваньНизькийСереднійВисокий (крихкі тести)

Антипаттерн: Fake, який не fake — поширена помилка, коли розробник називає fake-об'єкт, який насправді є stub або mock. Якщо ваш InMemoryUserRepository не містить логіки (фільтрації, сортування) — це не fake, а stub з in-memory зберіганням. Fake відрізняється від stub саме наявністю виконуваної логіки. Якщо fake-репозиторій просто повертає те, що в нього поклали, і не обробляє дані — використовуйте mock або stub.

Практичне правило вибору test double

Практична рекомендація — починайте з fake для кожного репозиторію або сервісу. Якщо fake складніший за 50 рядків — розбийте на кілька класів. Якщо fake взагалі не потрібен (тест перевіряє тільки один сценарій із фіксованими даними) — використовуйте stub. Якщо тест перевіряє, що метод був викликаний із певними параметрами — mock. Не оптимізуйте вибір заздалегідь: напишіть fake, і якщо він виявиться надлишковим, замініть на stub у конкретному тесті.

Створення Fake-об'єктів на Android для Room і Retrofit

Fake-репозиторій для Room — типовий приклад fake на Android. Продакшен-реалізація UserRepository використовує Room DAO із SQLite-запитами. Fake-версія зберігає дані в MutableList або HashMap і реалізує ті самі методи: getUser(id), saveUser(user), deleteUser(id). Fake містить логіку пошуку, фільтрації та сортування — таку ж, як у продакшен-репозиторії, але без SQL. Це дозволяє тестувати ViewModel і UseCase без налаштування Room-бази.

kotlin
class FakeUserRepository : UserRepository {

    private val users = mutableListOf<User>()

    override suspend fun getUser(id: String): User? {
        return users.find { it.id == id }
    }

    override suspend fun saveUser(user: User) {
        val index = users.indexOfFirst { it.id == user.id }
        if (index >= 0) users[index] = user
        else users.add(user)
    }

    override suspend fun search(query: String): List<User> {
        return users.filter {
            it.name.contains(query, ignoreCase = true)
        }
    }
}

Fake для Retrofit API — замість MockWebServer (який stub, а не fake) можна створити реалізацію ApiService, яка повертає дані з in-memory колекції. Різниця: MockWebServer перехоплює HTTP і повертає JSON, а fake-ApiService працює на рівні Kotlin-інтерфейсу без серіалізації. Fake швидший (немає парсингу JSON) і простіший у налагодженні (працює в тому ж процесі, типізований). Підходить для тестів, де не важлива HTTP-семантика (статус-коди, заголовки).

FakeSharedPreferences для швидких тестів

— ще один частий сценарій. Продакшен SharedPreferences пише на диск через commit/apply. Fake-версія зберігає пари ключ-значення в HashMap і миттєво повертає дані. Підтримує ті самі методи: getString, putString, getInt, putInt, clear. Для Jetpack DataStore аналог — FakeDataStore з in-memory сховищем. Такі fake прискорюють тести в десятки разів, тому що немає операцій запису на диск.

Fake-реалізації на iOS з in-memory сховищами

Fake на Swift — будується через протоколи. Продакшен-клас реалізує протокол із реальною логікою (CoreData, URLSession). Fake-структура реалізує той самий протокол з in-memory сховищем і спрощеною логікою. Swift — мова з value-семантикою, тому fake-структури імутабельні та безпечні в багатопотокових тестах. Це дає перевагу перед Android-аналогами: не потрібно синхронізувати доступ до in-memory даних.

swift
protocol UserRepositoryProtocol {
    func getUser(id: String) async -> User?
    func saveUser(user: User) async
}

final class FakeUserRepository: UserRepositoryProtocol {
    private var storage: [String: User] = [:]

    func getUser(id: String) async -> User? {
        return storage[id]
    }

    func saveUser(user: User) async {
        storage[user.id] = user
    }
}

final class UserViewModelTests: XCTestCase {
    func test_save_and_load() async {
        let fake = FakeUserRepository()
        let vm = UserViewModel(repository: fake)
        let user = User(id: "1", name: "Alice")

        await vm.saveUser(user)
        let loaded = await vm.getUser(id: "1")

        XCTAssertEqual(loaded?.name, "Alice")
    }
}

Fake для CoreData — в iOS-проектах можна створити in-memory NSPersistentContainer, передавши description.type = NSInMemoryStoreType. Це повноцінний CoreData stack, але працюючий в пам'яті. Такий fake дозволяє тестувати NSFetchRequest, предикати та сортування без створення SQLite-файлу. Швидкість: тести на in-memory CoreData виконуються в 5—10 разів швидше, ніж на дисковому аналогу. Недолік: потрібно налаштовувати NSManagedObjectModel щоразу.

FakeURLProtocol — підклас URLProtocol для перехоплення мережевих запитів на iOS. Реєструється через URLProtocol.registerClass(fakeProtocol). Всередині містить in-memory словник URL -> Data і повертає дані без реального запиту. Відмінність від stub: FakeURLProtocol може перевіряти тіло запиту, заголовки та повертати різні відповіді залежно від вхідних даних. Це fake, тому що містить логіку маршрутизації запитів.

Паттерни використання Fake в мобільних проектах

Fake як Test Fixture — виносьте fake-класи в загальний test-модуль (androidTest/sharedTest або TestSupport). Всі тести в проекті використовують один і той самий InMemoryUserRepository. Це усуває дублювання налаштування mock-об'єктів у кожному тесті та гарантує однакову поведінку. Зміна логіки fake оновлює всі тести одночасно. В IT Sectr ми зберігаємо fake-класи в sharedTest/java/com/itSectr/fake/ і підключаємо через implementation project(:sharedTest).

Fake з попередньо встановленими даними — часто тестам потрібен репозиторій, що вже містить деякі записи. Рішення: фабричний метод fakeWithData(vararg items) або вбудований метод addDefaultData(). Фабрика створює fake, наповнює його типовими даними та повертає готовий до використання об'єкт. Це зменшує boilerplate в тестах: замість налаштування mock-викликів тест просто викликає FakeUserRepository.withUsers(alice, bob).

Fake з підрахунком викликів — іноді потрібно перевірити не тільки стан, але й кількість звернень. Fake може містити лічильники: saveCallCount, getUserCallCount. Тест перевіряє лічильник після виконання. Це компроміс між чистим fake (перевірка стану) та mock (перевірка взаємодії). Лічильники не перевіряють аргументи та послідовність викликів — тільки кількість. Для перевірки аргументів використовуйте mock.

Fake з Callback — для тестування асинхронних сценаріїв fake може приймати callback при кожному виклику: beforeGetUser, afterSaveUser. Це дозволяє симулювати затримки, помилки або перевіряти проміжні стани. Такий підхід корисний для тестування loading-станів UI: fake робить паузу в 100 мс, а тест перевіряє, що екран показує лоадер. В продакшені callback відсутній — це чисто тестова функціональність.

Часті запитання

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

Fake містить робочу логіку — фільтрує, сортує, рахує. Stub повертає тільки заздалегідь задані відповіді без логіки. Якщо об'єкт має розгалуження (if/else, when) — це fake. Якщо він містить тільки return values — це stub. Fake дорожчий у підтримці, але дає більш реалістичні тести.

Коли fake може бути шкідливим?

Коли fake-логіка не збігається з продакшен-логікою. Наприклад, FakeUserRepository використовує case-sensitive пошук, а продакшен — case-insensitive. Тест проходить, а в реальності баг. Рішення: тестуйте fake-логіку окремо або використовуйте fakes тільки для інтерфейсів із простою логікою (CRUD-операції). Для складної логіки пишіть інтеграційні тести з реальною БД.

Fake це те саме, що in-memory database?

In-memory database — один із варіантів fake. Room.inMemoryDatabaseBuilder() створює in-memory SQLite, який поводиться як продакшен-БД. Це повноцінний fake. Але fake може бути і на рівні репозиторію (без SQL), і на рівні мережі (FakeApiService). In-memory БД — окремий випадок fake, коли логіка максимально наближена до реальної.

Чи можна комбінувати Fake і Mock в одному тесті?

Так, але з обережністю. Fake для репозиторію (дані), Mock для AnalyticsTracker (верифікація подій). Розділення по шарах: fake для шару даних, mock для шару аналітики/логіювання. Не робіть один об'єкт одночасно fake і mock — це порушує Single Responsibility Principle і заплутує тест.

Як тестувати сам Fake?

Тестуйте fake тими самими тестами, що й продакшен-реалізацію. Якщо у вас є UserRepositoryTest, який перевіряє save, get, delete — запустіть його двічі: з FakeUserRepository і з RealUserRepository. Це гарантує, що fake повторює поведінку продакшен-класу. Якщо fake починає поводитися інакше — тест впаде на обох реалізаціях.

Підсумки

  • Fake — робоча спрощена реалізація залежності з реальною бізнес-логікою та in-memory зберіганням
  • Відмінність від Stub — fake містить логіку (фільтрацію, сортування), stub тільки повертає дані
  • Швидкість — fake працює в 100—1000 разів швидше за продакшен-реалізацію без I/O-операцій
  • Android — InMemoryUserRepository, FakeDataStore, in-memory Room через Room.inMemoryDatabaseBuilder
  • iOS — protocol-based fake, in-memory CoreData, FakeURLProtocol для HTTP-перехоплення
  • Найкраща практика — виносьте fake в загальний test-модуль і використовуйте у всіх тестах проекту
  • Тестуйте fake — запускайте одні й ті самі тести на fake та продакшен-реалізації для перевірки консистентності

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

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

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

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