Test Doubles — co to jest, rodzaje i zastosowanie

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

Test Doubles — to obiekty zastępcze używane w testach jednostkowych zamiast rzeczywistych zależności. Termin został wprowadzony przez Gerarda Meszarosa w książce „xUnit Test Patterns“ (2007) jako pojęcie ogólne dla Mock, Stub, Fake, Spy i Dummy. Według Martina Fowlera (2024), Test Doubles pozwalają na izolację testowanego komponentu od jego otoczenia, czyniąc testy deterministycznymi, szybkimi i niezależnymi od zewnętrznych serwisów.

Najważniejsze

  • Test Doubles — ogólny termin dla wszystkich typów obiektów zastępczych w testowaniu
  • Mock sprawdza interakcje: jakie metody zostały wywołane i z jakimi argumentami
  • Stub zwraca z góry określone wartości bez sprawdzania wywołań
  • Fake — uproszczona działająca implementacja (np. baza danych in-memory)
  • Spy rejestruje wywołania do późniejszej weryfikacji, Dummy wypełnia parametry

Czym są Test Doubles?

Test Doubles — to termin pochodzący z przemysłu motoryzacyjnego (kaskader, „double“ dla aktorów), przeniesiony do tworzenia oprogramowania. Jak kaskader zastępuje aktora w niebezpiecznej scenie, Test Double zastępuje rzeczywisty komponent w scenariuszu testowym. Jest to konieczne, gdy rzeczywista zależność jest niedostępna, wolna, niedeterministyczna lub ma skutki uboczne.

Pojęcie Test Double obejmuje pięć konkretnych typów, z których każdy rozwiązuje swoje zadanie. Typologia Meszarosa jest kanoniczna i używana we wszystkich nowoczesnych podręcznikach testowania. Różnica między typami polega na stopniu kontroli i weryfikacji: od prostego wypełniania parametrów (Dummy) do pełnego sprawdzenia sekwencji wywołań (Mock).

Po co są Test Doubles

Głównym celem Test Doubles jest izolacja testowanego modułu. W programowaniu mobilnym rzeczywiste zależności to serwery API, bazy danych, system plików, czujniki urządzenia, usługi systemowe (LocationManager, Camera, Bluetooth). Bezpośrednie używanie tych komponentów sprawia, że testy są wolne, kruche i zależne od otoczenia. Według Google Testing Blog (2023), dobrze izolowane testy jednostkowe wykonują się w ciągu milisekund, a integracyjne — w sekundach i minutach.

Pięć typów Test Doubles

Klasyfikacja Gerarda Meszarosa obejmuje pięć typów Test Doubles, różniących się zachowaniem i celem użycia. Zrozumienie różnic między nimi to podstawa poprawnego testowania jednostkowego.

Dummy

Dummy — to obiekt przekazywany do testowanej metody, ale nigdy nie używany. Dummy jest potrzebny tylko do spełnienia sygnatury metody. W Kotlin jest to często null, emptyList() lub obiekt z atrapami. Dummy nie powinien zawierać żadnej logiki — jeśli zostanie wywołany, test powinien zakończyć się niepowodzeniem.

Fake

Fake — to uproszczona, ale działająca implementacja interfejsu. W przeciwieństwie do Mock i Stub, Fake zawiera rzeczywistą logikę biznesową, ale w uproszczonej formie. Klasyczny przykład — InMemoryUserRepository, który przechowuje dane w HashMap zamiast w bazie danych. Fake jest używany, gdy trzeba przetestować logikę zależną od stanu, ale bez narzutu rzeczywistej infrastruktury.

TypPrzeznaczeniePrzykład
DummyWypełnić parametrnull, pusty obiekt
FakeDziałająca uproszczona implementacjaInMemoryRepository
StubZwrócić stałą wartośćwhen(api.getUser()).thenReturn(user)
SpyZarejestrować wywołania do sprawdzeniaverify(spy).save(user)
MockSprawdzić interakcjęverify(mock).sendEmail(email)

Stub

Stub zwraca z góry określone wartości na konkretne wywołania. Stub nie sprawdza, czy został wywołany — po prostu dostarcza dane. W Mockito Stub tworzy się przez when(method).thenReturn(value). Stub jest idealny do testowania, gdy zależność ma zwrócić konkretną wartość, ale sam fakt wywołania nie jest istotny.

Spy

Spy — to otoczka wokół rzeczywistego obiektu, która rejestruje wszystkie wywołania do późniejszej weryfikacji. W przeciwieństwie do Mock, Spy deleguje wywołania do rzeczywistego obiektu, ale pozwala sprawdzić, że one nastąpiły. W Mockito Spy tworzy się przez spy(realObject). Spy jest przydatny do częściowego mockowania, gdy chce się używać rzeczywistego obiektu, ale sprawdzić niektóre wywołania.

Mock

Mock — to obiekt z predefiniowanymi oczekiwaniami wywołań. Mock sprawdza, czy określone metody zostały wywołane z określonymi argumentami i w określonej kolejności. W przeciwieństwie do Stub, Mock koncentruje się na weryfikacji zachowania, a nie na zwracaniu danych. Mock — to najpotężniejszy i najczęściej używany typ Test Double w programowaniu mobilnym.

Mock i Stub: kluczowe różnice

Różnica między Mock a Stub często powoduje zamieszanie nawet u doświadczonych programistów. Główna różnica polega na celu: Stub sprawdza stan (state verification), Mock sprawdza zachowanie (behavior verification).

Stub odpowiada na pytanie: „czy kod zwrócił poprawny wynik?“. Mock odpowiada na pytanie: „czy kod wywołał odpowiednie metody z odpowiednimi argumentami?“. W programowaniu mobilnym Stub jest używany, gdy ważny jest rezultat (np. dane z repozytorium), a Mock — gdy ważne są efekty uboczne (np. wysłanie emaila, zapis do bazy danych).

kotlin
// Stub: sprawdzanie stanu
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)

// Mock: sprawdzanie zachowania
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }

Przykłady Test Doubles w Kotlin

Praktyczne przykłady wszystkich pięciu typów Test Doubles w Kotlin z użyciem MockK — najpopularniejszej biblioteki mocking dla projektów Android.

Fake: InMemoryUserRepository

kotlin
class InMemoryUserRepository : UserRepository {
    private val store = mutableMapOf<String, User>()

    override fun save(user: User) {
        store[user.email] = user
    }

    override fun findByEmail(email: String): User? {
        return store[email]
    }
}

Stub + Mock: test UseCase

kotlin
class RegisterUseCaseTest {
    private val api = mockk<AuthApi>()
    private val repo = spyk(InMemoryUserRepository())
    private val useCase = RegisterUseCase(api, repo)

    fun `register user successfully`() = runTest {
        // Stub: zwracamy stałą odpowiedź API
        coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")

        val result = useCase.execute("test@test.com")

        // Verify: sprawdzamy, czy użytkownik został zapisany
        verify { repo.save(any()) }
        assertTrue(result.isSuccess())
    }
}

Dummy: test z nieużywanym parametrem

kotlin
data class Logger(val appContext: Context, val format: FormatType)

fun `test logger with dummy context`() {
    // Dummy: Context nie jest używany wewnątrz Logger
    val dummyContext = mockk<Context>()
    val logger = Logger(dummyContext, FormatType.JSON)
    assertEquals(FormatType.JSON, logger.format)
}

Kiedy jaki typ stosować w programowaniu mobilnym

Wybór typu Test Double zależy od tego, co dokładnie jest testowane: stan, zachowanie czy integracja. W programowaniu mobilnym na Android i iOS powstały następujące zalecenia.

Dla ViewModel i UseCase

Przy testowaniu ViewModel używaj Mock dla zależności, które powodują efekty uboczne (repozytoria, analityka, nawigacja), oraz Stub dla zależności, które zwracają dane (klienty API, ContentProvider). Pozwala to sprawdzić, czy ViewModel poprawnie obsługuje zarówno udane, jak i błędne scenariusze.

Dla Repository i Warstwy Danych

Na poziomie Repository preferowane są Fake (implementacje bazy danych in-memory) i Stub (stałe odpowiedzi API). Fake pozwala sprawdzić logikę buforowania i trybu offline bez konfiguracji SQLite. Stub symuluje różne kody HTTP: 200, 404, 500, timeout.

  • Testy jednostkowe logiki biznesowej — Mock dla wszystkich zewnętrznych zależności, Dummy dla nieużywanych parametrów
  • Testy integracyjne — Fake zamiast Mock (sprawdzamy, czy komponenty działają razem)
  • Testy UI — Stub dla odpowiedzi API (przez MockWebServer lub WireMock)
  • Testy buforowania — Fake dla bazy danych (in-memory zamiast Room/SQLite)
  • Testy asynchroniczności — Mock z obsługą korutyn (MockK + Turbine dla Flow)

Typowe błędy przy użyciu obiektów zastępczych

Nieprawidłowe używanie Test Doubles — jedna z najczęstszych przyczyn kruchych testów, które psują się przy każdej refaktoryzacji.

Over-mocking: nadmierne używanie Mock

Najczęstszy błąd — mockowanie wszystkiego. Jeśli każda zależność w teście jest zastąpiona przez Mock, test przestaje sprawdzać rzeczywiste zachowanie. Mock powinien być tylko dla zewnętrznych zależności (sieć, baza danych, system plików, usługi systemowe). Wewnętrzne komponenty aplikacji (Value Object, data class, proste narzędzia) nie powinny być zastępowane.

Under-specification: niewystarczająca specyfikacja

Drugi błąd — tworzenie Mock bez określenia oczekiwań. Jeśli metoda jest wywoływana bez every / when, Mock zwraca wartość domyślną (null, 0, false). Może to prowadzić do fałszywie pozytywnych testów, gdy Mock milcząco zwraca null, a test interpretuje to jako poprawne zachowanie.

Over-verification: nadmierna weryfikacja

Trzeci błąd — sprawdzanie każdego wywołania każdego Mocka. Verify powinien być używany tylko dla wywołań krytycznych z punktu widzenia logiki biznesowej. Nadmierna weryfikacja czyni testy kruchymi: zmiana kolejności wywołań w kodzie produkcyjnym psuje testy bez zmiany zachowania.

Często zadawane pytania

Jaka jest różnica między Mock a Stub?

Stub zwraca dane i sprawdza stan (co zostało zwrócone), a Mock sprawdza zachowanie (jakie metody zostały wywołane). Stub = „zwróć X“, Mock = „sprawdź, czy wywołano Y z argumentem Z“. W rzeczywistych testach jeden obiekt często pełni rolę zarówno Stub, jak i Mock jednocześnie.

Kiedy używać Fake zamiast Mock?

Fake jest preferowany nad Mock, gdy testowana jest logika zależna od stanu: buforowanie, tryb offline, transakcje. Fake (implementacja in-memory) pozwala sprawdzić te scenariusze bez kruchych wywołań verify. Mock lepiej nadaje się do sprawdzania wysyłania danych: analityka, powiadomienia push, email.

Która biblioteka Test Doubles jest najlepsza dla Androida?

Dla projektów Android w Kotlin zalecana jest MockK. Obsługuje korutyny, suspend-funkcje, sealed class i extension-funkcje bez dodatkowej konfiguracji. Dla projektów w Javie nadal standardem jest Mockito — najpopularniejsza biblioteka z obszerną dokumentacją.

Jak testować Kotlin Flow z Test Doubles?

Do testowania Kotlin Flow używaj biblioteki Turbine w parze z MockK. Turbine upraszcza sprawdzanie emisji Flow: można sprawdzić kolejność wartości, zakończenie strumienia i wyjątki. Stub dla Flow zwraca flowOf(value), Mock sprawdza, czy Flow został zebrany.

Czy dopuszczalne jest używanie Test Doubles w testach UI?

Tak, ale na poziomie odpowiedzi API, a nie komponentów UI. Biblioteki MockWebServer (OkHttp) i WireMock pozwalają podmieniać odpowiedzi HTTP w testach UI. Same komponenty UI (Compose, SwiftUI Views) nie powinny być zastępowane — ich zachowanie testuje się przez screenshot-test i Espresso.

Podsumowanie

  • Test Doubles — ogólny termin dla pięciu typów obiektów zastępczych: Mock, Stub, Fake, Spy, Dummy
  • Mock sprawdza zachowanie (verify), Stub zwraca dane (thenReturn), Fake — działa jak uproszczona rzeczywista implementacja
  • Spy otacza rzeczywisty obiekt i rejestruje wywołania, Dummy wypełnia nieużywane parametry
  • Typologia Gerarda Meszarosa — kanoniczna klasyfikacja używana we wszystkich nowoczesnych frameworkach mocking
  • Dla projektów Kotlin zalecany jest MockK, dla Java — Mockito, dla iOS — Cuckoo lub OHHTTPStubs
  • Typowe błędy: over-mocking (zastępowanie wszystkiego), under-specification (nieokreślone oczekiwania), over-verification (nadmierne verify)
  • Fake preferowany nad Mock przy testowaniu logiki ze stanem — buforowania, trybu offline i transakcji

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ż