Stub: co to jest, rodzaje i zastosowanie w testowaniu

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

Stub (zastępnik, imitacja) — obiekt testowy zwracający predefiniowane odpowiedzi na wywołania metod zamiast rzeczywistej implementacji. W programowaniu mobilnym staby izolują testowany moduł od zapytań sieciowych, bazy danych i systemu plików, umożliwiając sprawdzanie logiki bez konfiguracji środowiska. W przeciwieństwie do mock, stub nie weryfikuje zachowania — on tylko dostarcza dane. Więcej w artykule Martina Fowlera o test doubles.

Najważniejsze

  • Stub — zastępnik zwracający zadane wartości na wywołania metod bez logiki
  • Izolacja — staby wyłączają rzeczywiste zależności: API, bazy danych, pliki, czujniki
  • Różnica od Mock — stub nie weryfikuje wywołań, on tylko podmienia odpowiedź
  • Android — MockWebServer (OkHttp) jako stub dla HTTP, MockK.constantAnswer dla Kotlin
  • iOS — OCMock i protokoły Swift z implementacjami testowymi jako staby

Co to jest Stub i czym różni się od innych test doubles?

Stub — to obiekt-zastępnik, który zastępuje rzeczywistą zależność w teście i zwraca z góry określone wartości na konkretne wywołania. Termin został wprowadzony w klasyfikacji Gerarda Meszarosa (2007) w książce „xUnit Test Patterns". Stub należy do kategorii test doubles — obiektów zastępujących rzeczywiste komponenty podczas testowania. Głównym celem staba jest dostarczenie testowanemu blokowi przewidywalnych danych, usuwając niepewność systemów zewnętrznych.

Zasada działania — test konfiguruje staba przed wykonaniem: „gdy metoda getUsers() zostanie wywołana, zwróć tę listę użytkowników". Stub nie zawiera logiki biznesowej, nie sprawdza kolejności wywołań ani nie zapisuje historii wywołań. Po prostu stoi w miejscu rzeczywistego komponentu i wydaje to, co mu kazano. W kontekście testowania Androida oznacza to, że klient OkHttp nie wykonuje rzeczywistego żądania do serwera, a otrzymuje odpowiedź z MockWebServer skonfigurowanego jako stub.

  • Stub — zwraca dane, nie sprawdza wywołań
  • Mock — zwraca dane i weryfikuje zachowanie (verify)
  • Fake — działająca uproszczona implementacja z prawdziwą logiką
  • Spy — opakowuje rzeczywisty obiekt, rejestrując wywołania
  • Dummy — przekazywany, ale nieużywany (null, pusty obiekt)

Kiedy stosować — staby są optymalne do testowania warstwy UI (ViewModel, Presenter) i logiki biznesowej (UseCase, Interactor), gdzie trzeba sprawdzić reakcję na konkretne dane: lista pusta, serwer zwrócił błąd 500, token wygasł. Każdy przypadek, w którym test wymaga określonego stanu wejściowego, to zadanie dla staba. Dla każdego scenariusza testowego tworzona jest własna konfiguracja staba, co czyni testy czytelnymi i przewidywalnymi.

Klasyfikacja test doubles według Meszarosa

Gerard Meszaros (2007) w książce „xUnit Test Patterns" wyróżnił pięć typów test doubles: dummy, stub, spy, mock, fake. Każdy typ rozwiązuje swoje zadanie. Dummy — przekazywany, ale nieużywany. Stub — zwraca dane. Spy — rejestruje wywołania. Mock — weryfikuje zachowanie. Fake — zawiera uproszczoną logikę. Zrozumienie tej klasyfikacji pomaga programiście wybrać odpowiednie narzędzie dla każdego scenariusza testowego.

Gdzie stosuje się staby w testowaniu aplikacji mobilnych

Staby dla zapytań sieciowych

Zapytania sieciowe — najczęstszy scenariusz użycia stabów. Aplikacja wykonuje wywołania HTTP do API, a w teście trzeba sprawdzić reakcję na różne odpowiedzi: udany JSON, błąd 401 (nieautoryzowany), timeout, pusta tablica. MockWebServer (OkHttp) na Androidzie i URLProtocol (iOS) pełnią rolę stabów, zwracając predefiniowane odpowiedzi HTTP bez rzeczywistego połączenia z serwerem. Przyspiesza to testy z sekund do milisekund.

Baza danych — Room (Android) i CoreData (iOS) mają warianty in-memory, ale ich konfiguracja i tak wymaga czasu. Stub zamiast repozytorium zwraca z góry przygotowane listy Entity, nie dotykając bazy danych. Jest to szczególnie skuteczne do testowania ViewModel, gdzie trzeba sprawdzić sortowanie, filtrowanie lub transformację danych. Test wykonuje się w milisekundach niezależnie od ilości danych.

Usługi systemowe — LocationManager, SensorManager, SharedPreferences wymagają rzeczywistego urządzenia lub emulatora. Stub dla LocationProvider zwraca zadane współrzędne, dla SensorManager — stałe wartości akcelerometru. Na iOS analogiem jest CLLocationManager z testową implementacją delegata. Bez stabów takie testy wymagają fizycznego urządzenia z określonymi warunkami.

System plików i pamięć podręczna — ładowanie obrazów, buforowanie odpowiedzi, praca z plikami konfiguracyjnymi — wszystkie te operacje zależą od stanu dysku. Stub dla FileManager lub ImageCache zwraca sukces/błąd bez odczytu rzeczywistych plików. Eliminuje to fałszywe upadki testów z powodu niezgodności ścieżek lub uprawnień na różnych maszynach programistów.

Stub vs Mock vs Fake: kluczowe różnice

Podział odpowiedzialności — trzy typy test doubles rozwiązują różne zadania. Stub: „daj mi dane". Mock: „sprawdź, czy mnie wywołano". Fake: „działam jak prawdziwy, tylko prościej". Różnica jest krytyczna dla czytelności testów: jeśli test używa mock tam, gdzie potrzebny jest stub, jest przeciążony wywołaniami verify, niezwiązanymi z testowanym scenariuszem.

CechaStubMockFake
PrzeznaczenieDostarczyć daneSprawdzić interakcjęUproszczona implementacja
LogikaNieNieTak (ale uproszczona)
WeryfikacjaNieTak (verify)Pośrednia (przez stan)
ElastycznośćNiska — sztywne odpowiedziŚredniaWysoka — logika się dostosowuje
SzybkośćMaksymalnaWysokaŚrednia
PrzykładMockWebServer zwraca JSONMockito.verify(repository).save()InMemoryRepository z HashMap

Praktyczna zasada — jeśli test sprawdza, jakie dane otrzymał testowany komponent — użyj stuba. Jeśli test sprawdza, czy komponent wywołał metodę zależności z prawidłowymi argumentami — użyj mocka. Jeśli po prostu chcesz zastąpić bazę danych tablicą hash — to jest fake. Mieszanie typów w jednym teście czyni go kruchym: przy zmianie implementacji trzeba będzie przepisać zarówno stuba, jak i logikę verify.

Antywzorzec: Stub z verify

Stub z verify — częsty błąd, gdy programista konfiguruje stuba, a następnie dodaje verify(stub).method(). Stub z definicji nie powinien być weryfikowany — do weryfikacji służy mock. Jeśli musisz sprawdzić, że metoda została wywołana z konkretnymi argumentami, użyj Mockito.mock() zamiast Mockito.stub(). To rozdzielenie utrzymuje intencję testu jasną dla innych programistów.

Implementacja stabów na Androidzie z MockWebServer i MockK

MockWebServer — biblioteka OkHttp do tworzenia zastępników HTTP na Androidzie i JVM. Uruchamia lokalny serwer HTTP na określonym porcie, który przechwytuje żądania klienta OkHttp i zwraca predefiniowane odpowiedzi. Konfiguracja zajmuje trzy linie: utworzyć serwer, dodać odpowiedź do kolejki (enqueue), uruchomić. Test może sekwencyjnie dodawać do kolejki wiele odpowiedzi dla scenariuszy z paginacją lub ponownymi próbami.

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 — alternatywa dla Mockito dla Kotlin z natywnym wsparciem dla korutyn, funkcji rozszerzających i klas zapieczętowanych (sealed). Staby w MockK tworzy się przez coEvery (dla funkcji suspend) i every (dla zwykłych). W przeciwieństwie do MockWebServer, MockK zastępuje poszczególne metody-zależności, a nie całą warstwę HTTP. Jest to wygodne do testów jednostkowych UseCase lub Interactor, gdzie zależnościami są abstrakcje repozytoriów.

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 — do testów integracyjnych używaj MockWebServer (przechwytuje rzeczywisty HTTP), do testów jednostkowych — MockK (zastępuje interfejsy). Nie zastępuj tego, czego nie testujesz: jeśli test sprawdza Repository, nie zastępuj w nim klienta OkHttp — użyj prawdziwego MockWebServer na poziomie HTTP. Ta zasada utrzymuje testy trafne i zmniejsza ich kruchość przy refaktoryzacji.

Implementacja stabów na iOS z OCMock i protokołami

Protokoły Swift jako staby — w natywnym podejściu iOS stub implementuje się przez podstawienie testowej struktury zgodnej z protokołem zależności. Zamiast rzeczywistego NetworkService test otrzymuje StubNetworkService, który zwraca stałe dane. Swift jest językiem ze statycznym typowaniem, więc stub musi być zgodny z tym samym protokołem co rzeczywista usługa. Kompilator gwarantuje, że stub implementuje wszystkie wymagane metody.

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 dla Objective-C — biblioteka do tworzenia stabów i mocków w starszych projektach iOS. OCMock obsługuje metody stub z argumentami i wartościami zwracanymi. Nowoczesne projekty w Swift preferują podejście oparte na protokołach z ręcznymi stubami — daje to kontrolę nad każdą metodą i nie wymaga zewnętrznych zależności. OCMock pozostaje opcją dla projektów, gdzie protokolizacja wszystkich zależności jest ekonomicznie nieuzasadniona.

URLProtocol dla zastępników HTTP — systemowy mechanizm iOS do przechwytywania zapytań sieciowych przez podklasę URLProtocol. Test rejestruje niestandardowy URLProtocol, który przechwytuje URLSession i zwraca odpowiedzi stuba. Zaleta w porównaniu z ręcznymi stubami: nie trzeba zmieniać architektury aplikacji — URLSession pozostaje rzeczywisty, ale dane są podmieniane na poziomie protokołu. Wada: trudniejsze debugowanie niż w przypadku jawnej usługi stub.

Często zadawane pytania

Czym Stub różni się od Mock?

Stub zwraca predefiniowane dane i nie sprawdza faktu wywołania. Mock dodatkowo weryfikuje, że metoda została wywołana z prawidłowymi argumentami (verify). Stub odpowiada na pytanie „co zwrócić", Mock — na pytanie „czy było wywołanie". Używaj stuba do sprawdzania stanu, mocka — do weryfikacji interakcji.

Kiedy używać Fake zamiast Stub?

Fake jest potrzebny, gdy test wymaga działającej (choć uproszczonej) implementacji — na przykład bazy danych in-memory zamiast Room. Stub nadaje się do pojedynczych scenariuszy z predefiniowanymi danymi. Jeśli powtarzasz ten sam stub w 10 testach — najprawdopodobniej potrzebujesz Fake. Fake zmniejsza powielanie, ponieważ logika żyje w jednej klasie.

Czy można zastępować statyczne metody?

Na Androidzie — MockK dla obiektów Kotlin (object) obsługuje mockkObject(), w tym statyczne metody klas Javy przez mockkStatic(). Na iOS — statyczne metody Swift nie są bezpośrednio zastępowalne; używaj protokołów i DI, aby zamienić wywołanie static na metodę instancyjną protokołu. Statyczne staby to dług techniczny, którego należy unikać w nowym kodzie.

Jak zastępować zapytania sieciowe na Androidzie?

Używaj MockWebServer (OkHttp) — działa jako lokalny serwer HTTP, który dodaje odpowiedzi do kolejki (enqueue). Dla Retrofit wystarczy zmienić bazowy URL na localhost:8080. Dla Ktor użyj MockEngine — wbudowanego mechanizmu do podmiany HttpStatement. Oba podejścia działają bez rzeczywistego internetu i dają pełną kontrolę nad kodem statusu, treścią i nagłówkami odpowiedzi.

Stub vs Spy — jaka jest różnica?

Spy opakowuje rzeczywisty obiekt i rejestruje wywołania, a Stub całkowicie zastępuje obiekt stałymi odpowiedziami. Spy pozwala częściowo korzystać z rzeczywistej implementacji (pozostałe metody działają bez zmian), a stub — nie. Jeśli musisz sprawdzić, że metoda została wywołana, ale część logiki powinna się wykonać — użyj spy, a nie stuba.

Podsumowanie

  • Stub — obiekt-zastępnik zwracający predefiniowane odpowiedzi na wywołania metod podczas testowania
  • Izolacja zależności — staby zastępują zapytania sieciowe, bazy danych, usługi systemowe i system plików
  • Różnica od Mock — stub nie weryfikuje wywołań, on tylko zwraca dane bez sprawdzania zachowania
  • Narzędzia Android — MockWebServer dla HTTP, MockK dla interfejsów Kotlin z obsługą korutyn
  • Narzędzia iOS — staby oparte na protokołach w Swift, URLProtocol dla HTTP, OCMock dla Objective-C
  • Nie mieszaj ról — nie dodawaj verify do stuba, używaj mocka do weryfikacji wywołań
  • Stub + MockWebServer — standardowe podejście do testów integracyjnych bez rzeczywistego serwera

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ż