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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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