Stub: What It Is, Types, and Usage in Testing

Author: IT Sectr Published: 2026-04-10 Reading time: 9 min

Stub (stub, test double) — a test object that returns predefined responses to method calls instead of a real implementation. In mobile development, stubs isolate the tested module from network requests, databases, and file systems, allowing logic verification without environment setup. Unlike mock, stub does not verify behavior — it only provides data. For more details, see Martin Fowler's article on test doubles.

Key Takeaways

  • Stub — a test double that returns predefined values on method calls without logic
  • Isolation — stubs disable real dependencies: API, DB, files, sensors
  • Difference from Mock — stub does not verify calls, it only substitutes the response
  • Android — MockWebServer (OkHttp) as stub for HTTP, MockK.constantAnswer for Kotlin
  • iOS — OCMock and Swift protocols with test implementations as stubs

What Is a Stub and How Does It Differ from Other Test Doubles?

Stub is a test double object that replaces a real dependency in a test and returns predefined values for specific calls. The term was introduced in Gerard Meszaros's classification (2007) in the book “xUnit Test Patterns”. Stub belongs to the category of test doubles — objects that substitute real components during testing. The main purpose of a stub is to provide the tested unit with predictable data, removing uncertainty from external systems.

How it works — the test configures the stub before execution: “when getUsers() method is called, return this list of users”. Stub contains no business logic, does not verify call order, and does not record call history. It simply stands in place of the real component and returns what it was told. In Android testing context, this means: the OkHttp client does not make a real request to the server, but receives a response from MockWebServer configured as a stub.

  • Stub — returns data, does not verify calls
  • Mock — returns data and verifies behavior (verify)
  • Fake — a working simplified implementation with real logic
  • Spy — wraps a real object, recording calls
  • Dummy — passed but not used (null, empty object)

When to use — stubs are optimal for testing the UI layer (ViewModel, Presenter) and business logic (UseCase, Interactor), where you need to verify reactions to specific data: empty list, server returned error 500, token expired. Any case where the test requires a specific input state is a task for stub. Each test scenario gets its own stub configuration, making tests readable and predictable.

Test Doubles Classification by Meszaros

Gerard Meszaros (2007) in the book “xUnit Test Patterns” identified five types of test doubles: dummy, stub, spy, mock, fake. Each type serves its purpose. Dummy — passed but not used. Stub — returns data. Spy — records calls. Mock — verifies behavior. Fake — contains simplified logic. Understanding this classification helps developers choose the right tool for each test scenario.

Where Are Stubs Used in Mobile Application Testing

Stubs for Network Requests

Network requests — the most common scenario for using stubs. The application makes HTTP calls to the API, and the test needs to verify reactions to different responses: successful JSON, 401 error (unauthorized), timeout, empty array. MockWebServer (OkHttp) on Android and URLProtocol (iOS) act as stubs, returning predefined HTTP responses without a real server connection. This speeds up tests from seconds to milliseconds.

Database — Room (Android) and CoreData (iOS) have in-memory variants, but setting them up still takes time. Stub instead of the repository returns pre-prepared Entity lists without touching the database. This is especially effective for testing the ViewModel, where you need to verify sorting, filtering, or data transformation. The test runs in milliseconds regardless of data volume.

System services — LocationManager, SensorManager, SharedPreferences require a real device or emulator. Stub for LocationProvider returns predefined coordinates, for SensorManager — fixed accelerometer values. On iOS, the equivalent is CLLocationManager with a test delegate implementation. Without stubs, such tests require a physical device with specific conditions.

File system and cache — image loading, response caching, configuration file handling — all these operations depend on disk state. Stub for FileManager or ImageCache returns success/error without reading real files. This eliminates false test failures due to path mismatches or access rights on different developer machines.

Stub vs Mock vs Fake: Key Differences

Division of responsibility — three types of test doubles solve different tasks. Stub: “give me data”. Mock: “verify that I was called”. Fake: “I work like the real one, just simpler”. The difference is critical for test readability: if a test uses mock where stub is needed, it is overloaded with verify calls unrelated to the scenario being tested.

CharacteristicStubMockFake
PurposeProvide dataVerify interactionSimplified implementation
LogicNoneNoneYes (but simplified)
VerificationNoneYes (verify)Indirect (via state)
FlexibilityLow — hardcoded responsesMediumHigh — logic adapts
SpeedMaximumHighMedium
ExampleMockWebServer returns JSONMockito.verify(repository).save()InMemoryRepository with HashMap

Practical rule — if the test is checking what data the component under test received — use stub. If the test is checking whether the component called a dependency method with the right arguments — use mock. If you simply want to replace a database with a hash table — that is a fake. Mixing types in one test makes it brittle: when the implementation changes, you will have to rewrite both stub and verify logic.

Anti-pattern: Stub with verify

Stub with verify — a common mistake where a developer sets up a stub and then adds verify(stub).method(). Stub by definition should not be verified — there is mock for verification. If you need to check that a method was called with specific arguments, use Mockito.mock() instead of Mockito.stub(). This separation keeps the test intent clear for other developers.

Implementing Stubs on Android with MockWebServer and MockK

MockWebServer — an OkHttp library for creating HTTP stubs on Android and JVM. It starts a local HTTP server on a specified port that intercepts OkHttp client requests and returns predefined responses. Setup takes three lines: create the server, enqueue the response, start it. The test can sequentially enqueue multiple responses for scenarios with pagination or retries.

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 — an alternative to Mockito for Kotlin with first-class support for coroutines, extension functions, and sealed classes. Stubs in MockK are created via coEvery (for suspend functions) and every (for regular ones). Unlike MockWebServer, MockK stubs individual dependency methods rather than the entire HTTP layer. This is convenient for unit tests of UseCase or Interactor, where dependencies are repository abstractions.

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 — for integration tests use MockWebServer (intercepts real HTTP), for unit tests use MockK (stubs interfaces). Do not stub what you are not testing: if the test verifies the Repository, do not stub the OkHttp client inside it — use a real MockWebServer at the HTTP level. This rule keeps tests relevant and reduces brittleness during refactoring.

Implementing Stubs on iOS with OCMock and Protocols

Swift protocols as stubs — in the iOS-native approach, stub is implemented by substituting a test structure that conforms to the dependency protocol. Instead of a real NetworkService, the test receives a StubNetworkService that returns fixed data. Swift is a statically typed language, so the stub must conform to the same protocol as the real service. The compiler guarantees that the stub implements all required methods.

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 for Objective-C — a library for creating stubs and mocks in legacy iOS projects. OCMock supports stub methods with arguments and return values. Modern Swift projects prefer a protocol-based approach with manual stubs — this gives control over each method and does not require external dependencies. OCMock remains an option for projects where protocolizing all dependencies is economically unfeasible.

URLProtocol for HTTP stubs — a system mechanism in iOS for intercepting network requests through a URLProtocol subclass. The test registers a custom URLProtocol that intercepts URLSession and returns stub responses. The advantage over manual stubs: you do not need to change the application architecture — URLSession remains real, but data is substituted at the protocol level. The downside: harder to debug than an explicit stub service.

Frequently Asked Questions

How is Stub different from Mock?

Stub returns predefined data and does not verify whether a call occurred. Mock additionally verifies that the method was called with the correct arguments (verify). Stub answers the question “what to return”, Mock answers “was the call made”. Use stub for state verification, mock for interaction verification.

When to use Fake instead of Stub?

Fake is needed when the test requires a working (even if simplified) implementation — for example, an in-memory database instead of Room. Stub is suitable for single scenarios with predefined data. If you are repeating the same stub in 10 tests — you most likely need a Fake. Fake reduces duplication because the logic lives in a single class.

Can static methods be stubbed?

On Android — MockK for Kotlin objects (object) supports mockkObject(), including Java class static methods via mockkStatic(). On iOS — Swift static methods cannot be stubbed directly; use protocols and DI to replace a static call with an instance method of a protocol. Static stubs are technical debt and should be avoided in new code.

How to stub network requests on Android?

Use MockWebServer (OkHttp) — it works as a local HTTP server that enqueues responses. For Retrofit, simply replace the base URL with localhost:8080. For Ktor, use MockEngine — a built-in mechanism for substituting HttpStatement. Both approaches work without real internet and give full control over status code, body, and response headers.

Stub vs Spy — what is the difference?

Spy wraps a real object and records calls, while Stub completely replaces the object with fixed responses. Spy allows partial use of the real implementation (other methods work as is), whereas stub does not. If you need to verify that a method was called but part of the logic should still execute — use spy, not stub.

Summary

  • Stub — a test double that returns predefined responses to method calls during testing
  • Dependency isolation — stubs replace network requests, databases, system services, and file systems
  • Difference from Mock — stub does not verify calls, it only returns data without behavior checking
  • Android tools — MockWebServer for HTTP, MockK for Kotlin interfaces with coroutine support
  • iOS tools — protocol-based stubs in Swift, URLProtocol for HTTP, OCMock for Objective-C
  • Do not mix roles — do not add verify to stub, use mock for call verification
  • Stub + MockWebServer — standard approach for integration tests without a real server

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also