Stub: шта је то, врсте и примена у тестирању

Аутор: IT Sectr Објављено: 2026-04-10 Време читања: 9 мин

Stub (стаб, замена) — тестни објекат који враћа унапред дефинисане одговоре на позиве метода уместо стварне имплементације. У мобилном развоју, стабови изолују тестирани модул од мрежних захтева, базе података и система датотека, омогућавајући проверу логике без подешавања окружења. За разлику од mock, stub не проверава понашање — он само обезбеђује податке. Детаљније у чланку Martin Fowler-a о test doubles.

Главно

  • Stub — замена која враћа задате вредности на позиве метода без логике
  • Изолација — стабови искључују стварне зависности: API, базу података, датотеке, сензоре
  • Разлика од Mock — стаб не верификује позиве, он само замењује одговор
  • Android — MockWebServer (OkHttp) као стаб за HTTP, MockK.constantAnswer за Kotlin
  • iOS — OCMock и Swift протоколи са тестним имплементацијама као стабови

Шта је Stub и по чему се разликује од других test doubles?

Stub — је објекат-замена који замењује стварну зависност у тесту и враћа унапред задате вредности на одређене позиве. Термин је уведен у класификацији Gerard Meszaros (2007) у књизи „xUnit Test Patterns". Stub припада категорији test doubles — објеката који замењују стварне компоненте током тестирања. Основна сврха стаба је да тестираном блоку обезбеди предвидљиве податке, уклањајући несигурност спољних система.

Принцип рада — тест конфигурише стаб пре извршења: „када метод getUsers() буде позван, врати ову листу корисника". Stub не садржи пословну логику, не проверава редослед позива и не бележи историју приступа. Он једноставно стоји на месту стварне компоненте и даје оно што му је речено. У контексту Android тестирања то значи да OkHttp клијент не прави стварни захтев ка серверу, већ добија одговор из MockWebServer-а конфигурисаног као стаб.

  • Stub — враћа податке, не проверава позиве
  • Mock — враћа податке и проверава понашање (verify)
  • Fake — радна поједностављена имплементација са стварном логиком
  • Spy — обавија стварни објекат, бележећи позиве
  • Dummy — прослеђује се, али се не користи (null, празан објекат)

Када користити — стабови су оптимални за тестирање UI слоја (ViewModel, Presenter) и пословне логике (UseCase, Interactor), где треба проверити реакцију на одређене податке: листа празна, сервер је вратио грешку 500, токен је истекао. Сваки случај где тест захтева одређено улазно стање — задатак је за стаб. За сваки тестни сценариј креира се своја конфигурација стаба, што чини тестове читљивим и предвидљивим.

Класификација test doubles према Meszaros-у

Gerard Meszaros (2007) у књизи „xUnit Test Patterns" је издвојио пет типова test doubles: dummy, stub, spy, mock, fake. Сваки тип решава свој задатак. Dummy — прослеђује се, али се не користи. Stub — враћа податке. Spy — бележи позиве. Mock — проверава понашање. Fake — садржи поједностављену логику. Разумевање ове класификације помаже програмеру да изабере прави алат за сваки тестни сценариј.

Где се користе стабови у тестирању мобилних апликација

Стабови за мрежне захтеве

Мрежни захтеви — најчешћи сценариј примене стабова. Апликација шаље HTTP позиве ка API-ју, а у тесту треба проверити реакцију на различите одговоре: успешан JSON, грешка 401 (неовлашћен), тајмаут, празан низ. MockWebServer (OkHttp) на Android-у и URLProtocol (iOS) делују као стабови, враћајући унапред дефинисане HTTP одговоре без стварне везе са сервером. Ово убрзава тестове са секунди на милисекунде.

База података — Room (Android) и CoreData (iOS) имају in-memory варијанте, али њихово подешавање ипак захтева време. Stab уместо репозиторијума враћа унапред припремљене листе Entity, не дирајући базу података. Ово је посебно ефикасно за тестирање ViewModel-а, где треба проверити сортирање, филтрирање или трансформацију података. Тест се извршава за милисекунде без обзира на количину података.

Системски сервиси — LocationManager, SensorManager, SharedPreferences захтевају прави уређај или емулатор. Stab за LocationProvider враћа задате координате, за SensorManager — фиксне вредности акцелерометра. На iOS-у аналог је CLLocationManager са тестном имплементацијом делегата. Без стабова такви тестови захтевају физички уређај са одређеним условима.

Систем датотека и кеш — учитавање слика, кеширање одговора, рад са конфигурационим датотекама — све ове операције зависе од стања диска. Stab за FileManager или ImageCache враћа успех/грешку без читања стварних датотека. Ово елиминише лажне падове тестова због неусаглашености путања или дозвола на различитим машинама програмера.

Stub vs Mock vs Fake: кључне разлике

Подела одговорности — три типа test doubles решавају различите задатке. Stub: „дај ми податке". Mock: „провери да ли су ме позвали". Fake: „радим као прави, само једноставније". Разлика је критична за читљивост тестова: ако тест користи mock тамо где је потребан stub, преоптерећен је verify позивима који нису повезани са тестираним сценаријем.

КарактеристикаStubMockFake
НаменаОбезбедити податкеПроверити интеракцијуПоједностављена имплементација
ЛогикаНемаНемаИма (али поједностављена)
ВерификацијаНемаИма (verify)Индиректна (кроз стање)
ФлексибилностНиска — чврсти одговориСредњаВисока — логика се прилагођава
БрзинаМаксималнаВисокаСредња
ПримерMockWebServer враћа JSONMockito.verify(repository).save()InMemoryRepository са HashMap

Практично правило — ако тест проверава које податке је тестирана компонента добила — користите stub. Ако тест проверава да ли је компонента позвала метод зависности са исправним аргументима — користите mock. Ако само желите да замените базу података хеш табелом — то је fake. Мешање типова у једном тесту чини га крхким: при промени имплементације мораћете да препишете и stub и verify логику.

Антипатерн: Stub са verify

Stub са verify — честа грешка када програмер конфигурише stub, а затим дода verify(stub).method(). Stub по дефиницији не треба да се верификује — за верификацију постоји mock. Ако треба да проверите да је метод позван са одређеним аргументима, користите Mockito.mock() уместо Mockito.stub(). Ово раздвајање одржава намеру теста јасном за друге програмере.

Имплементација стабова на Android-у са MockWebServer и MockK

MockWebServer — OkHttp библиотека за креирање HTTP стабова на Android-у и JVM. Она покреће локални HTTP сервер на одређеном порту који пресреће захтеве OkHttp клијента и враћа унапред дефинисане одговоре. Подешавање траје три линије: креирати сервер, додати одговор у ред (enqueue), покренути. Тест може секвенцијално да дода више одговора у ред за сценарије са пагинацијом или поновних покушаја.

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 — алтернатива за Mockito за Kotlin са first-class подршком за корутине, extension функције и sealed класе. Стабови у MockK се креирају кроз coEvery (за suspend функције) и every (за обичне). За разлику од MockWebServer-а, MockK замењује појединачне методе-зависности, а не цео HTTP слој. Ово је згодно за unit тестове UseCase или Interactor-а, где су зависности апстракције репозиторијума.

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 — за интеграционе тестове користите MockWebServer (пресреће прави HTTP), за unit тестове — MockK (замењује интерфејсе). Немојте замењивати оно што не тестирате: ако тест проверава Repository, немојте замењивати OkHttp клијента унутар њега — користите прави MockWebServer на нивоу HTTP-а. Ово правило одржава тестове релевантним и смањује крхкост при рефакторисању.

Имплементација стабова на iOS-у са OCMock и протоколима

Swift протоколи као стабови — у iOS-native приступу стаб се имплементира кроз подметање тестне структуре која одговара протоколу зависности. Уместо правог NetworkService, тест добија StubNetworkService који враћа фиксне податке. Swift је језик са статичким типовима, па стаб мора да одговара истом протоколу као и прави сервис. Компајлер гарантује да стаб имплементира све потребне методе.

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 за Objective-C — библиотека за креирање стабова и мокова у legacy iOS пројектима. OCMock подржава stub методе са аргументима и повратним вредностима. Савремени пројекти у Swift-у преферирају protocol-based приступ са ручним стабовима — ово даје контролу над сваким методом и не захтева спољне зависности. OCMock остаје опција за пројекте где протоколизација свих зависности није економски оправдана.

URLProtocol за HTTP стабове — системски механизам iOS-а за пресретање мрежних захтева кроз подкласу URLProtocol. Тест региструје прилагођени URLProtocol који пресреће URLSession и враћа stub одговоре. Предност у односу на ручне стабове: не треба мењати архитектуру апликације — URLSession остаје прави, али се подаци замењују на нивоу протокола. Недостатак: теже се дебагује него експлицитни stub сервис.

Често постављана питања

По чему се Stub разликује од Mock?

Stub враћа унапред дефинисане податке и не проверава чињеницу позива. Mock додатно верификује да је метод позван са исправним аргументима (verify). Stub одговара на питање „шта вратити", Mock — на питање „да ли је позван". Користите stub за проверу стања, mock — за проверу интеракције.

Када користити Fake уместо Stub?

Fake је потребан када тесту треба радна (макар и поједностављена) имплементација — на пример, in-memory база података уместо Room-а. Stub је погодан за појединачне сценарије са унапред дефинисаним подацима. Ако понављате исти stub у 10 тестова — највероватније вам треба Fake. Fake смањује дуплирање јер логика живи у једној класи.

Могу ли се стабовати статички методи?

На Android-у — MockK за Kotlin објекте (object) подржава mockkObject(), укључујући статичке методе Java класа кроз mockkStatic(). На iOS-у — статички методи Swift-а се не стабују директно; користите протоколе и DI да замените static позив методом инстанце протокола. Статички стабови су технички дуг и треба их избегавати у новом коду.

Како стабовати мрежне захтеве на Android-у?

Користите MockWebServer (OkHttp) — ради као локални HTTP сервер који додаје одговоре у ред (enqueue). За Retrofit је довољно променити базни URL на localhost:8080. За Ktor користите MockEngine — уграђени механизам за замену HttpStatement. Оба приступа раде без правог интернета и дају потпуну контролу над статусним кодом, телом и заглављима одговора.

Stub vs Spy — у чему је разлика?

Spy обавија прави објекат и бележи позиве, док Stub у потпуности замењује објекат фиксним одговорима. Spy омогућава делимично коришћење стварне имплементације (остали методи раде као и раније), а stub — не. Ако треба да проверите да је метод позван, али део логике треба да се изврши — користите spy, а не stub.

Завршни преглед

  • Stub — објекат-замена који враћа унапред дефинисане одговоре на позиве метода током тестирања
  • Изолација зависности — стабови замењују мрежне захтеве, базе података, системске сервисе и систем датотека
  • Разлика од Mock — стаб не верификује позиве, он само враћа податке без провере понашања
  • Android алати — MockWebServer за HTTP, MockK за Kotlin интерфејсе са подршком за корутине
  • iOS алати — protocol-based стабови у Swift-у, URLProtocol за HTTP, OCMock за Objective-C
  • Не мешајте улоге — не додајте verify на stub, користите mock за верификацију позива
  • Stub + MockWebServer — стандардни приступ за интеграционе тестове без правог сервера

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође