Mock — What It Is, Mock Objects, and Libraries for Testing

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

Mock is a substitute object that mimics the behavior of a real component and allows verifying interactions with it. Unlike Stub, which simply returns a predetermined value, Mock records the fact of method invocation, the arguments passed, and the number of calls. According to Mockito (2024), Mock is the most popular type of Test Double in Java and Kotlin projects, used in more than 70% of unit tests in mobile applications.

Key Takeaways

  • Mock — an object that verifies interaction: which methods were called, with what arguments, and how many times
  • Mockito — the most popular library for creating Mocks in Java and Android projects
  • MockK — an alternative to Mockito for Kotlin with native support for coroutines and sealed classes
  • Behavior verification — the key difference between Mock and Stub: Mock verifies behavior, not state
  • Over-mocking — the main anti-pattern: mocks should only be used for external dependencies

What Is a Mock?

Mock is an object created by a mocking framework (Mockito, MockK, EasyMock) that simulates an interface or class and records all calls to its methods. The developer sets expectations: method X will be called with arguments Y and will return Z. After the test executes, the Mock verifies that the expectations matched the actual calls.

The term comes from the theatrical metaphor of Test Doubles: a Mock is an “impersonator” that does not just stand on stage (like a Dummy) but performs a role and verifies whether the interaction with it was correct. If the code under test did not call the method the Mock expected, or called it with incorrect arguments — the test fails with a violated expectation message.

How a Mock Works

A Mock is created through the framework factory: mockk<MyInterface>() or Mockito.mock(MyClass.java). The framework generates a proxy object that intercepts all method calls. Each call is compared against predefined expectations. If the call matches an expectation — the specified value is returned. If not — the Mock returns a default value or throws an exception, depending on the configuration.

When a Mock Is Necessary

Mock is essential when the code under test interacts with components that have side effects: sending data to a server, writing to a database, logging, analytics, navigation, showing system dialogs. Without Mocks, these interactions cannot be verified without running real infrastructure. According to the Google Testing Blog, Mock is the only way to verify that an application actually sent an analytics event without spinning up a test server.

Mock vs. Stub: Detailed Comparison

The difference between Mock and Stub is one of the most debated topics in testing. Both types replace a real dependency, but in fundamentally different ways.

CriterionMockStub
Main QuestionWas the method called?What result was returned?
VerificationBehavior (verify)State (assert)
Data ReturnOptionalMandatory
Exampleverify(analytics).logEvent(“click”)assertEquals(5, repository.getCount())
When to UseSide effectsData return

Practical Rule: Mock or Not

A simple test to decide: ask yourself “if I delete this line of code, will the test fail?” If the test checks a return value — you need a Stub (assert-based verification). If the test checks that the code called a method with the correct arguments — you need a Mock (verify-based verification). This dichotomy follows from the Command-Query Separation pattern: methods that change state (commands) need Mocks; methods that return data (queries) need Stubs.

Mockito vs. MockK: Library Comparison

Choosing between Mockito and MockK is one of the first decisions when setting up the test stack for an Android project in Kotlin. Both libraries serve the same purpose, but with different approaches to Kotlin-specific features.

Mockito: Proven Classic

Mockito is the de facto standard for Java projects. Version 5.x supports mocking for final classes, static methods, and constructors thanks to the built-in MockMaker. For Kotlin projects, Mockito requires additional setup: mockito-kotlin extensions for improved syntax, mockito-inline for final classes. Mockito does not support Kotlin coroutines and suspend functions without additional adapters.

MockK: Kotlin-First Approach

MockK was created specifically for Kotlin. It natively supports coroutines (coEvery, coVerify), sealed classes, data classes, object singletons, and extension functions. MockK syntax uses DSL with lambda blocks, which feels natural in Kotlin code. MockK also supports property mocking without additional setup — this is important for Android projects using LiveData, StateFlow, and Delegates.

kotlin
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)

// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user

Performance Comparison

Benchmarks (JVM Benchmark, 2024) show that MockK creates mock objects 15–20% faster than Mockito for Kotlin projects thanks to direct work with Kotlin bytecode rather than Java Reflections. For projects with thousands of unit tests, the difference in build speed can be noticeable: MockK saves 30–60 seconds on a full test run in large projects.

Mock Test Examples in Kotlin

Let’s examine three scenarios: testing a ViewModel with Mock dependencies, testing a UseCase verifying API calls, and testing coroutines with coVerify.

Example 1: ViewModel with Mock Analytics

kotlin
class ProfileViewModelTest {
    private val analytics = mockk<AnalyticsService>()
    private val repo = mockk<UserRepository>()
    private val vm = ProfileViewModel(repo, analytics)

    fun `profile opened logs analytics event`() {
        every { analytics.logEvent("profile_opened") } returns Unit

        vm.onViewCreated()

        verify { analytics.logEvent("profile_opened") }
    }
}

Example 2: UseCase with Async Verification

kotlin
class SendMessageUseCaseTest {
    private val api = mockk<MessagingApi>()
    private val useCase = SendMessageUseCase(api)

    fun `send message with correct payload`() = runTest {
        val message = Message(text = "Hello", userId = 42)

        coEvery { api.sendMessage(any()) } returns MessageResult.Sent("msg_1")

        val result = useCase.execute(message)

        coVerify {
            api.sendMessage(match {
                it.text == "Hello" && it.userId == 42
            })
        }
        assertTrue(result is MessageResult.Sent)
    }
}

Example 3: Argument Verification with ArgumentCaptor

kotlin
class OrderUseCaseTest {
    private val api = mockk<OrderApi>()
    private val useCase = OrderUseCase(api)
    private val slot = slot<OrderRequest>()

    fun `order request contains correct items`() = runTest {
        coEvery { api.placeOrder(capture(slot)) } returns OrderResult.Placed("order_1")

        useCase.execute(listOf("item_a", "item_b"))

        assertEquals(2, slot.captured.items.size)
        assertEquals("item_a", slot.captured.items[0])
    }
}

Best Practices for Mock Testing

Effective use of Mock in mobile development requires discipline. Violating these rules turns tests into fragile obstacles that break with every refactoring.

Mock Only External Application Boundaries

A hard rule: Mocks should only be created for dependencies that cross the application boundary: API clients, databases, file systems, system services (LocationManager, BluetoothAdapter, Camera). Internal application classes — domain entities, Value Objects, simple utilities — should not be replaced with Mocks. Their behavior is tested through real objects.

One Assert / Verify per Test

Each test should contain exactly one logical check — either verify (for Mock) or assert (for Stub). Do not mix state and behavior verification in a single test. If you need to check both an API call and its result — create two separate tests with different names. This rule, known as “one assert per test”, dates back to Kent Beck’s recommendations (2002).

  • Use relaxUnitFun = true in MockK for methods returning Unit — otherwise Mock throws an exception on an unspecified call
  • Limit verify only to critical calls — do not verify every getter and setter, this makes tests fragile
  • Apply ArgumentMatchers thoughtfully — any() hides important details if the argument is critically important for business logic
  • Do not overuse verifyNoMoreInteractions — this method makes tests unnecessarily rigid to any changes in production code
  • Use @MockkAnnotations for automatic initialization of Mock objects — this reduces boilerplate and improves readability

Advanced Mock Testing Techniques

Beyond basic mocking, there are advanced techniques that solve specific tasks in mobile development: testing multithreading, verifying Flow state, and partial mocking of real objects.

Partial Mock with spyK

Spy (or partial mock) allows you to create an object that delegates calls to the real implementation but lets you override individual methods. In MockK, spyk is created based on a real class instance: val repo = spyk(InMemoryUserRepository()). Calls with expectations defined via every go through the Mock; the rest go through the real object. Spy is especially useful for testing legacy code where dependency injection hasn’t been implemented yet and you only need to override one method.

Testing StateFlow with Turbine

In modern Android projects using Jetpack Compose, ViewModel exposes state through StateFlow. MockK allows mocking Flow dependencies, and the Turbine library simplifies emission verification. The classic pattern: MockK for a UseCase returning a Flow, Turbine for verifying ViewModel emissions. This stack is recommended by the Android Testing documentation (Google, 2024) for projects using Kotlin Coroutines.

kotlin
class SearchViewModelTest {
    private val searchUseCase = mockk<SearchUseCase>()
    private val vm = SearchViewModel(searchUseCase)

    fun `search emits results`() = runTest {
        coEvery { searchUseCase.search("android") } returns
            flowOf(SearchResult.Success(listOf(Item("Android TDD"))))

        vm.search("android")

        vm.state.test {
            val state = awaitItem()
            assertTrue(state.items.isNotEmpty())
            cancelAndIgnoreRemainingEvents()
        }
    }
}

Frequently Asked Questions

How is Mock different from Mockito?

Mock is a concept, a type of Test Double that verifies behavior. Mockito is a library for creating Mock objects in Java and Android. Other libraries: MockK (Kotlin), EasyMock (Java), Cuckoo (iOS).

How does Mock work with Kotlin coroutines?

For testing suspend functions with Mock, use MockK (coEvery / coVerify) or Mockito with mockito-kotlin. MockK supports coroutines natively: coEvery defines the behavior of a suspend function, coVerify verifies its call inside a coroutine. All suspend calls must be executed inside runTest (kotlinx-coroutines-test).

Can a Mock return different values on repeated calls?

Yes. In MockK, use returnsMany: every { api.getData() } returnsMany listOf(response1, response2). In Mockito — a chain of thenReturn(value1).thenReturn(value2). This is useful for testing behavior with sequential calls returning different responses.

How do I clear Mock state between tests?

In MockK, use the @MockK annotation with relaxed = true and call clearMocks(mock) in the @After method. In MockitoMockito.reset(mock). Best practice: create a new Mock for each test via @Before to eliminate cross-test interference.

How does Mock handle sealed classes in Kotlin?

MockK works correctly with sealed classes: every { useCase() } returns Result.Success(data). Mockito does not support sealed classes directly, requiring workarounds. This is one reason why MockK is recommended for Kotlin projects over Mockito.

Summary

  • Mock — a Test Double type that verifies behavior (verify), not state (assert) of dependencies
  • Mockito — the standard for Java/Android, MockK — the Kotlin-first choice with support for coroutines and sealed classes
  • Main rule: Mock for external boundaries (network, DB, system services), real objects for internal classes
  • Over-mocking — the main anti-pattern: excessive dependency replacement makes tests fragile and unhelpful
  • One test — one logical check: verify for Mock or assert for Stub, but not both in the same test
  • ArgumentCaptor / slot — the correct way to verify Mock call arguments instead of blind any()
  • MockK is recommended for Kotlin projects: coEvery and coVerify work natively with coroutines without additional adapters

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