Fake — co to jest, przeznaczenie i jak używać w testowaniu

Autor: IT Sectr Opublikowano: 2026-04-10 Czas czytania: 9 min

Fake (fejk) — działająca uproszczona implementacja zależności, która zachowuje się jak prawdziwy komponent, ale wykorzystuje pamięć wewnętrzną (in-memory) lub inne lekkie mechanizmy zamiast produkcyjnej infrastruktury. W przeciwieństwie do stub, fake zawiera rzeczywistą logikę biznesową — sortowanie, filtrowanie, agregację — po prostu bez efektów zewnętrznych. In-memory baza danych zamiast Room lub HashMap zamiast SharedPreferences — klasyczne przykłady. Więcej w klasyfikacji test doubles Martina Fowlera.

Najważniejsze

  • Fake — uproszczona działająca implementacja z prawdziwą logiką, ale bez zewnętrznych zależności
  • In-memory przechowywanie — fake-repozytorium przechowuje dane w HashMap, a nie w bazie danych
  • Różnica od Stub — stub zwraca ustalone dane, fake zawiera wykonywalną logikę
  • Android — InMemoryUserRepository jako Fake do testowania ViewModel i UseCase
  • iOS — FakeNetworkSession z URLProtocol i danymi testowymi zamiast rzeczywistego serwera

Czym jest Fake i po co jest potrzebny w testowaniu?

Fake — to pełnowartościowa, ale uproszczona implementacja interfejsu, odpowiednia do testowania. Termin wprowadzony przez Gerarda Meszarosa (2007) w książce „xUnit Test Patterns”. W przeciwieństwie do stub, który zwraca sztywno określone odpowiedzi, fake zawiera wykonywalny kod: może sortować listę, filtrować według warunku, liczyć liczbę rekordów. Jedyna różnica od implementacji produkcyjnej — fake działa na danych in-memory i nie wykonuje rzeczywistych operacji I/O.

Główna zaleta — szybkość. Testy z fake wykonują się w milisekundach, ponieważ nie ma dostępu do dysku, sieci ani bazy danych. In-memory HashMap działa 100-1000 razy szybciej niż Room lub CoreData. Jednocześnie fake sprawdza rzeczywistą logikę biznesową: sortowanie, filtrowanie, agregację — wszystko to, czego stub nie może sprawdzić, ponieważ stub zwraca tylko to, co mu kazano. Fake daje pewność, że kod poprawnie przetwarza dane, a nie tylko otrzymuje z góry określoną odpowiedź.

Kiedy Fake jest lepszy od Stub

Fake jest lepszy od Stub — jeśli testowany komponent wykonuje kilka operacji na danych (pobrał, przefiltrował, posortował, zapisał), stub będzie wymagał konfiguracji każdego wywołania osobno. Fake zawiera logikę wewnętrznie — test po prostu wywołuje metody i sprawdza wynik. W IT Sectr używamy fake dla wszystkich repozytoriów w testach jednostkowych: fake-repozytorium z HashMap pokrywa 90% scenariuszy bez konfiguracji Mockito czy MockK.

Fake vs Stub vs Mock: kiedy co wybrać

Kryterium wyboru — określ, co sprawdza test: stan czy interakcję. Jeśli test sprawdza stan (wynik działania) i używa logiki — potrzebujesz fake. Jeśli test potrzebuje tylko danych wejściowych bez logiki — wystarczy stub. Jeśli test sprawdza fakt wywołania metody — potrzebujesz mock. Mieszanie typów test doubles w jednym teście utrudnia zrozumienie i zwiększa kruchość.

KryteriumFakeStubMock
Posiadanie logikiTak (uproszczona)NieNie
SzybkośćWysokaMaksymalnaWysoka
Sprawdzanie zachowaniaPoośredniaNieTak (verify)
UtrzymanieJedna klasa na interfejsKonfiguracja pod każdy testKonfiguracja pod każdy test
RealizmWysoki (kod działa)Niski (sztywne dane)Średni
Ryzyko fałszywych alarmówNiskieŚrednieWysokie (kruche testy)

Antywzorzec: Fake, który nie jest fake — częsty błąd, gdy programista nazywa obiekt fake, który w rzeczywistości jest stub lub mock. Jeśli twój InMemoryUserRepository nie zawiera logiki (filtrowania, sortowania) — to nie jest fake, a stub z przechowywaniem in-memory. Fake różni się od stub właśnie posiadaniem wykonywalnej logiki. Jeśli fake-repozytorium po prostu zwraca to, co do niego włożono, i nie przetwarza danych — użyj mock lub stub.

Praktyczna zasada wyboru test double

Praktyczne zalecenie — zaczynaj od fake dla każdego repozytorium lub serwisu. Jeśli fake jest bardziej skomplikowany niż 50 linii — podziel na kilka klas. Jeśli fake w ogóle nie jest potrzebny (test sprawdza tylko jeden scenariusz z ustalonymi danymi) — użyj stub. Jeśli test sprawdza, czy metoda została wywołana z określonymi parametrami — użyj mock. Nie optymalizuj wyboru z góry: napisz fake, a jeśli okaże się nadmiarowy, zastąp go stub w konkretnym teście.

Tworzenie obiektów Fake na Androidzie dla Room i Retrofit

Fake-repozytorium dla Room — typowy przykład fake na Androidzie. Implementacja produkcyjna UserRepository używa Room DAO z zapytaniami SQLite. Wersja fake przechowuje dane w MutableList lub HashMap i implementuje te same metody: getUser(id), saveUser(user), deleteUser(id). Fake zawiera logikę wyszukiwania, filtrowania i sortowania — taką samą jak w repozytorium produkcyjnym, ale bez SQL. Pozwala to testować ViewModel i UseCase bez konfiguracji bazy 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 dla Retrofit API — zamiast MockWebServer (który jest stub, a nie fake) można utworzyć implementację ApiService, która zwraca dane z kolekcji in-memory. Różnica: MockWebServer przechwytuje HTTP i zwraca JSON, a fake-ApiService działa na poziomie interfejsu Kotlin bez serializacji. Fake jest szybszy (brak parsowania JSON) i prostszy w debugowaniu (działa w tym samym procesie, typowany). Nadaje się do testów, gdzie nie jest ważna semantyka HTTP (kody statusu, nagłówki).

FakeSharedPreferences do szybkich testów

— kolejny częsty scenariusz. Produkcyjne SharedPreferences zapisuje na dysk przez commit/apply. Wersja fake przechowuje pary klucz-wartość w HashMap i błyskawicznie zwraca dane. Obsługuje te same metody: getString, putString, getInt, putInt, clear. Dla Jetpack DataStore odpowiednik — FakeDataStore z pamięcią in-memory. Takie fake przyspieszają testy dziesiątki razy, ponieważ nie ma operacji zapisu na dysk.

Implementacje Fake na iOS z pamięciami in-memory

Fake w Swift — buduje się przez protokoły. Klasa produkcyjna implementuje protokół z rzeczywistą logiką (CoreData, URLSession). Struktura fake implementuje ten sam protokół z pamięcią in-memory i uproszczoną logiką. Swift jest językiem z semantyką wartości, dlatego struktury fake są niemutowalne i bezpieczne w testach wielowątkowych. Daje to przewagę nad odpowiednikami na Androida: nie trzeba synchronizować dostępu do danych 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 dla CoreData — w projektach iOS można utworzyć in-memory NSPersistentContainer, podając description.type = NSInMemoryStoreType. To pełnoprawny stos CoreData, ale działający w pamięci. Taki fake pozwala testować NSFetchRequest, predykaty i sortowania bez tworzenia pliku SQLite. Szybkość: testy na in-memory CoreData wykonują się 5-10 razy szybciej niż na odpowiedniku dyskowym. Wada: trzeba konfigurować NSManagedObjectModel za każdym razem.

FakeURLProtocol — podklasa URLProtocol do przechwytywania zapytań sieciowych na iOS. Rejestruje się przez URLProtocol.registerClass(fakeProtocol). Wewnątrz zawiera słownik in-memory URL -> Data i zwraca dane bez rzeczywistego zapytania. Różnica od stub: FakeURLProtocol może sprawdzać treść zapytania, nagłówki i zwracać różne odpowiedzi w zależności od danych wejściowych. To jest fake, ponieważ zawiera logikę routingu zapytań.

Wzorce używania Fake w projektach mobilnych

Fake jako Test Fixture — wynieś klasy fake do wspólnego modułu testowego (androidTest/sharedTest lub TestSupport). Wszystkie testy w projekcie używają tego samego InMemoryUserRepository. Eliminuje to duplikowanie konfiguracji obiektów mock w każdym teście i gwarantuje jednolite zachowanie. Zmiana logiki fake aktualizuje wszystkie testy jednocześnie. W IT Sectr przechowujemy klasy fake w sharedTest/java/com/itSectr/fake/ i podłączamy przez implementation project(:sharedTest).

Fake z predefiniowanymi danymi — często testy potrzebują repozytorium, które już zawiera pewne rekordy. Rozwiązanie: metoda fabryki fakeWithData(vararg items) lub wbudowana metoda addDefaultData(). Fabryka tworzy fake, wypełnia go typowymi danymi i zwraca gotowy do użycia obiekt. Zmniejsza to boilerplate w testach: zamiast konfiguracji wywołań mock test po prostu wywołuje FakeUserRepository.withUsers(alice, bob).

Fake z licznikiem wywołań — czasami trzeba sprawdzić nie tylko stan, ale także liczbę odwołań. Fake może zawierać liczniki: saveCallCount, getUserCallCount. Test sprawdza licznik po wykonaniu. To kompromis pomiędzy czystym fake (sprawdzanie stanu) a mock (sprawdzanie interakcji). Liczniki nie sprawdzają argumentów ani kolejności wywołań — tylko liczbę. Do sprawdzania argumentów używaj mock.

Fake z Callback — do testowania scenariuszy asynchronicznych fake może przyjmować callback przy każdym wywołaniu: beforeGetUser, afterSaveUser. Pozwala to symulować opóźnienia, błędy lub sprawdzać stany pośrednie. Takie podejście jest przydatne do testowania stanów ładowania UI: fake robi pauzę 100 ms, a test sprawdza, czy ekran pokazuje ładowarkę. W środowisku produkcyjnym callback nie występuje — to czysto testowa funkcjonalność.

Często zadawane pytania

Czym Fake różni się od Stub?

Fake zawiera działającą logikę — filtruje, sortuje, liczy. Stub zwraca tylko z góry określone odpowiedzi bez logiki. Jeśli obiekt ma rozgałęzienia (if/else, when) — to jest fake. Jeśli zawiera tylko return values — to jest stub. Fake jest droższy w utrzymaniu, ale daje bardziej realistyczne testy.

Kiedy fake może być szkodliwy?

Kiedy logika fake nie pokrywa się z logiką produkcyjną. Na przykład FakeUserRepository używa wyszukiwania case-sensitive, a produkcyjny — case-insensitive. Test przechodzi, a w rzeczywistości jest błąd. Rozwiązanie: testuj logikę fake osobno lub używaj fake tylko dla interfejsów z prostą logiką (operacje CRUD). Dla złożonej logiki pisz testy integracyjne z prawdziwą bazą danych.

Czy Fake to to samo co in-memory database?

In-memory database — jeden z wariantów fake. Room.inMemoryDatabaseBuilder() tworzy in-memory SQLite, który zachowuje się jak produkcyjna baza danych. To pełnoprawny fake. Ale fake może być również na poziomie repozytorium (bez SQL) i na poziomie sieci (FakeApiService). In-memory baza danych to szczególny przypadek fake, gdy logika jest maksymalnie zbliżona do rzeczywistej.

Czy można łączyć Fake i Mock w jednym teście?

Tak, ale ostrożnie. Fake dla repozytorium (dane), Mock dla AnalyticsTracker (weryfikacja zdarzeń). Podział według warstw: fake dla warstwy danych, mock dla warstwy analityki/logowania. Nie rób jednego obiektu jednocześnie fake i mock — narusza to zasadę pojedynczej odpowiedzialności i utrudnia zrozumienie testu.

Jak testować samego Fake?

Testuj fake tymi samymi testami, co implementację produkcyjną. Jeśli masz UserRepositoryTest, który sprawdza save, get, delete — uruchom go dwukrotnie: z FakeUserRepository i z RealUserRepository. Gwarantuje to, że fake powtarza zachowanie klasy produkcyjnej. Jeśli fake zacznie działać inaczej — test nie powiedzie się na obu implementacjach.

Podsumowanie

  • Fake — działająca uproszczona implementacja zależności z rzeczywistą logiką biznesową i przechowywaniem in-memory
  • Różnica od Stub — fake zawiera logikę (filtrowanie, sortowanie), stub tylko zwraca dane
  • Szybkość — fake działa 100-1000 razy szybciej niż implementacja produkcyjna bez operacji I/O
  • Android — InMemoryUserRepository, FakeDataStore, in-memory Room przez Room.inMemoryDatabaseBuilder
  • iOS — protocol-based fake, in-memory CoreData, FakeURLProtocol do przechwytywania HTTP
  • Najlepsza praktyka — wynieś fake do wspólnego modułu testowego i używaj we wszystkich testach projektu
  • Testuj fake — uruchamiaj te same testy na fake i implementacji produkcyjnej w celu sprawdzenia spójności

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również