Bird
Raised Fist0
Kotlinprogramming~15 mins

Mocking with MockK in Kotlin - Deep Dive

Choose your learning style10 modes available
Overview - Mocking with MockK
What is it?
Mocking with MockK means creating fake versions of objects or functions in Kotlin tests. These fake objects behave like the real ones but let you control their responses and check how they were used. This helps test parts of your code without relying on real dependencies. MockK is a popular Kotlin library designed to make mocking easy and clear.
Why it matters
Without mocking, tests would need real objects that might be slow, unreliable, or hard to set up. This makes tests fragile and slow. Mocking lets you isolate the code you want to test, making tests faster and more reliable. It also helps find bugs early by checking how your code interacts with other parts.
Where it fits
Before learning MockK, you should understand basic Kotlin programming and how to write simple tests. After mastering MockK, you can explore advanced testing topics like integration testing, test-driven development, and using other testing tools alongside MockK.
Mental Model
Core Idea
Mocking with MockK is like creating a controlled pretend version of a part of your program to safely test how your code behaves with it.
Think of it like...
Imagine you want to test how a chef cooks a meal, but you don't want to wait for real ingredients or a real kitchen. So, you use fake ingredients and a pretend kitchen that behave like the real ones but let you control everything. This way, you can focus on testing the chef's skills without distractions.
┌───────────────┐       ┌───────────────┐
│   Your Code   │──────▶│   Mock Object │
└───────────────┘       └───────────────┘
         │                      ▲
         │ Calls methods        │ Controls responses
         ▼                      │
┌──────────────────────────────┐
│ Real Object (not used in test)│
└──────────────────────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding What Mocking Means
🤔
Concept: Introduce the idea of mocking as creating fake objects to test code parts independently.
Mocking means making a fake version of something your code uses, like a database or a web service. Instead of using the real thing, you use a fake that acts like it but lets you decide what it does. This helps test your code without needing the real parts.
Result
You understand that mocking helps isolate code for testing by replacing real parts with controllable fakes.
Understanding mocking as controlled pretend objects is the foundation for writing clear and reliable tests.
2
FoundationSetting Up MockK in Kotlin Projects
🤔
Concept: Learn how to add MockK to your Kotlin project and write a simple mock.
To use MockK, add it to your project dependencies. Then, you can create a mock object using mockk() function. For example: val mockList = mockk>() This creates a fake list you can control in tests.
Result
You can create mock objects in Kotlin using MockK and prepare for writing tests.
Knowing how to set up MockK is essential to start using mocking in your Kotlin tests.
3
IntermediateControlling Mock Behavior with Stubbing
🤔Before reading on: do you think mocks return real data by default or nothing? Commit to your answer.
Concept: Learn how to tell mocks what to return when their methods are called.
Mocks don't do anything by default. You need to 'stub' them to return specific values. For example: val mockList = mockk>() every { mockList.size } returns 3 Now, calling mockList.size will return 3 in your tests.
Result
Mocks return controlled values, letting you test how your code reacts to different scenarios.
Knowing how to stub mocks lets you simulate different situations your code might face.
4
IntermediateVerifying Interactions with Mocks
🤔Before reading on: do you think mocks automatically check if methods were called, or do you need to ask them? Commit to your answer.
Concept: Learn how to check if your code called mock methods as expected.
MockK lets you verify if certain methods were called on mocks. For example: verify { mockList.add("item") } This checks if add("item") was called. If not, the test fails. This helps ensure your code interacts correctly with dependencies.
Result
You can confirm your code uses dependencies correctly by checking method calls on mocks.
Verifying interactions helps catch bugs where your code forgets to call important methods.
5
IntermediateMocking Functions and Coroutines
🤔Before reading on: do you think mocking suspend functions is the same as regular functions? Commit to your answer.
Concept: Learn how to mock Kotlin functions, including suspend functions used in coroutines.
MockK supports mocking suspend functions used in coroutines. You use coEvery and coVerify instead of every and verify. For example: coEvery { mockApi.fetchData() } returns "data" coVerify { mockApi.fetchData() } This lets you test asynchronous code easily.
Result
You can mock and verify asynchronous suspend functions, making coroutine testing straightforward.
Understanding coroutine-specific mocking is key to testing modern Kotlin code using coroutines.
6
AdvancedUsing Spies to Partially Mock Objects
🤔Before reading on: do you think spies replace the whole object or keep some real behavior? Commit to your answer.
Concept: Learn how to create spies that keep real behavior but let you mock some parts.
A spy wraps a real object but lets you override some methods. For example: val realList = mutableListOf("a", "b") val spyList = spyk(realList) every { spyList.size } returns 10 Now, spyList.size returns 10, but other methods behave normally.
Result
You can test code with real objects but control specific behaviors for precise testing.
Knowing when to use spies helps balance between real behavior and controlled testing.
7
ExpertHandling MockK Limitations and Advanced Features
🤔Before reading on: do you think MockK can mock private methods or constructors by default? Commit to your answer.
Concept: Explore MockK's advanced capabilities and limitations, including mocking private methods and static objects.
MockK can mock private methods and constructors using special annotations and setup, but it requires extra configuration. It also supports mocking object singletons and static methods, which many other libraries struggle with. However, mocking final classes or inline functions can be tricky and sometimes needs workarounds.
Result
You understand MockK's power and limits, enabling you to write robust tests and avoid pitfalls.
Knowing MockK's internals and limits prevents wasted time chasing impossible mocks and guides better test design.
Under the Hood
MockK works by creating proxy objects at runtime that intercept calls to mocked methods. When you stub a method, MockK stores the expected behavior and returns it when the method is called. For verification, MockK tracks all calls made to mocks during the test. For coroutines, it uses special coroutine-aware proxies to handle suspend functions. Internally, it uses bytecode manipulation to create these proxies, allowing mocking of final classes and objects.
Why designed this way?
MockK was designed specifically for Kotlin, which has features like final classes and coroutines that traditional Java mocking libraries struggle with. It uses bytecode manipulation to overcome Kotlin's restrictions and provide a smooth, Kotlin-friendly API. This design choice balances power and usability, making mocking natural for Kotlin developers.
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│ Your Test     │──────▶│ MockK Proxy   │──────▶│ Real Object   │
│ Calls Methods │       │ Intercepts    │       │ (Optional)    │
└───────────────┘       └───────────────┘       └───────────────┘
         │                      │                      ▲
         │                      │ Stores behavior      │ Calls real method if not stubbed
         ▼                      ▼                      │
┌─────────────────────────────────────────────────────────────┐
│                 Bytecode Manipulation Layer                 │
└─────────────────────────────────────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do mocks execute real code by default or do nothing? Commit to your answer.
Common Belief:Mocks run the real code of the object unless told otherwise.
Tap to reveal reality
Reality:Mocks do nothing by default and return default values unless you stub them.
Why it matters:Assuming mocks run real code can lead to tests that silently pass or fail because the mock doesn't behave as expected.
Quick: Can you mock private methods easily with MockK without extra setup? Commit to your answer.
Common Belief:MockK can mock any method, including private ones, without special configuration.
Tap to reveal reality
Reality:Mocking private methods requires extra annotations and setup; it's not automatic.
Why it matters:Trying to mock private methods without setup causes confusing errors and wasted time.
Quick: Does verifying a mock method call check the order of calls by default? Commit to your answer.
Common Belief:Verifying a method call also checks that calls happened in the exact order.
Tap to reveal reality
Reality:Verification checks only that calls happened, not their order unless explicitly specified.
Why it matters:Misunderstanding this can cause tests to miss bugs related to call order.
Quick: Can MockK mock Kotlin inline functions directly? Commit to your answer.
Common Belief:MockK can mock inline functions just like regular functions.
Tap to reveal reality
Reality:Inline functions are compiled differently and cannot be mocked directly by MockK.
Why it matters:Expecting to mock inline functions leads to frustration and forces redesign of code for testability.
Expert Zone
1
MockK's ability to mock final classes and objects is rare and requires bytecode manipulation, which can affect test performance subtly.
2
Using relaxed mocks can simplify tests by returning default values automatically, but overusing them can hide missing stubs and cause false positives.
3
MockK supports capturing lambda arguments passed to mocks, enabling deep inspection of interactions beyond simple method calls.
When NOT to use
MockK is not ideal when you want full integration tests with real dependencies or when mocking inline functions heavily. In such cases, use real implementations, dependency injection, or design code for easier testing without mocks.
Production Patterns
In production, MockK is used to isolate units by mocking external services, databases, or complex dependencies. Teams often combine MockK with coroutine testing libraries and use spies to partially mock objects. Advanced usage includes mocking singletons and verifying complex interaction sequences.
Connections
Dependency Injection
Builds-on
Understanding dependency injection helps you design code that is easier to mock and test with MockK.
Test-Driven Development (TDD)
Builds-on
MockK enables TDD by letting you write tests that define expected interactions before implementing real code.
Theatre Rehearsal
Analogy
Like actors rehearse with stand-ins before the real show, mocking lets your code practice interactions safely.
Common Pitfalls
#1Forgetting to stub a mock method leads to unexpected default returns.
Wrong approach:val mockList = mockk>() println(mockList.size) // No stubbing done
Correct approach:val mockList = mockk>() every { mockList.size } returns 5 println(mockList.size) // Prints 5
Root cause:Not realizing mocks return default values (like 0 or null) unless explicitly told what to return.
#2Trying to verify a method call that never happened causes test failure.
Wrong approach:verify { mockList.clear() } // But clear() was never called
Correct approach:mockList.clear() verify { mockList.clear() } // Now verification passes
Root cause:Misunderstanding that verification checks actual calls made during the test.
#3Using relaxed mocks everywhere hides missing stubs and test errors.
Wrong approach:val mockList = mockk>(relaxed = true) // No stubs, but test passes anyway
Correct approach:val mockList = mockk>() every { mockList.size } returns 3 // Test fails if size is called without stub
Root cause:Relaxed mocks return default values automatically, which can mask missing stubs.
Key Takeaways
Mocking with MockK lets you create fake objects to isolate and test your Kotlin code safely.
You control mock behavior by stubbing methods and verify interactions to ensure correct usage.
MockK supports Kotlin-specific features like coroutines and final classes, making it powerful for modern Kotlin testing.
Understanding MockK's limits and advanced features helps write robust tests and avoid common pitfalls.
Combining MockK with good design practices like dependency injection leads to cleaner, more maintainable tests.

Practice

(1/5)
1. What is the main purpose of using MockK in Kotlin testing?
easy
A. To create fake objects that simulate real ones for testing
B. To compile Kotlin code faster
C. To write UI layouts
D. To deploy Kotlin applications

Solution

  1. Step 1: Understand mocking concept

    Mocking means creating fake versions of objects to test code without using real dependencies.
  2. Step 2: Identify MockK's role

    MockK is a Kotlin library that helps create these fake objects easily for testing purposes.
  3. Final Answer:

    To create fake objects that simulate real ones for testing -> Option A
  4. Quick Check:

    MockK = mocking tool for fake objects [OK]
Hint: MockK is for faking objects in tests [OK]
Common Mistakes:
  • Confusing MockK with build or deployment tools
  • Thinking MockK is for UI design
  • Assuming MockK speeds up compilation
2. Which of the following is the correct way to create a mock of a class MyService using MockK?
easy
A. val mock = new MyService()
B. val mock = MockK<MyService>()
C. val mock = mockk<MyService>()
D. val mock = createMock<MyService>()

Solution

  1. Step 1: Recall MockK syntax for mocking

    MockK uses the function mockk<ClassName>() to create mocks.
  2. Step 2: Check each option

    val mock = mockk<MyService>() uses mockk<MyService>(), which is correct syntax. Others are invalid or not MockK functions.
  3. Final Answer:

    val mock = mockk<MyService>() -> Option C
  4. Quick Check:

    mockk<Class>() creates mock [OK]
Hint: Use mockk<Class>() to create mocks [OK]
Common Mistakes:
  • Using uppercase 'MockK' as a function
  • Trying to use 'new' keyword in Kotlin
  • Using non-existent functions like createMock
3. Given the following code snippet, what will be printed?
val mock = mockk<Calculator>()
every { mock.add(2, 3) } returns 10
println(mock.add(2, 3))
println(mock.add(1, 1))
medium
A. 10 Exception
B. 10 0
C. 10 null
D. 5 0

Solution

  1. Step 1: Understand mocking behavior setup

    The mock is told to return 10 when add(2, 3) is called.
  2. Step 2: Check calls and outputs

    Calling mock.add(2, 3) returns 10 as set. Calling mock.add(1, 1) has no behavior defined, so MockK throws an exception by default.
  3. Final Answer:

    10 Exception -> Option A
  4. Quick Check:

    Defined call returns 10; undefined call throws exception [OK]
Hint: Undefined mock calls throw exceptions by default [OK]
Common Mistakes:
  • Assuming undefined calls return 0 or null
  • Thinking all calls return the same mocked value
  • Ignoring that MockK throws exceptions on undefined calls
4. Identify the error in this MockK usage:
val mock = mockk<UserRepository>()
every { mock.getUser(any()) } returns User("Alice")
mock.getUser(1)
verify { mock.getUser(1) }
medium
A. Missing relaxed = true when creating mock
B. No error; code is correct
C. No verify block needed after calling mock
D. Incorrect use of any() matcher in every

Solution

  1. Step 1: Analyze mocking and stubbing

    The mock is created normally, and every sets behavior for any argument using any(), which is valid.
  2. Step 2: Check verification usage

    After calling mock.getUser(1), the verify block correctly checks that the method was called with argument 1.
  3. Final Answer:

    No error; code is correct -> Option B
  4. Quick Check:

    any() matcher and verify block used correctly [OK]
Hint: any() matcher works inside every; verify checks calls [OK]
Common Mistakes:
  • Thinking relaxed=true is always required
  • Confusing verify usage with unnecessary calls
  • Misusing any() matcher outside stubbing
5. You want to mock a Kotlin interface PaymentProcessor so that calling process(amount) returns "Success" only when amount is greater than 100, otherwise throws an exception. Which MockK setup achieves this?
hard
A. every { mock.process(match { it > 100 }) } returns "Success" every { mock.process(match { amount <= 100 }) } throws IllegalArgumentException()
B. every { mock.process(any()) } returns "Success"
C. every { mock.process(100) } returns "Success" every { mock.process(0) } throws IllegalArgumentException()
D. every { mock.process(gt(100)) } returns "Success" every { mock.process(ltEq(100)) } throws IllegalArgumentException()

Solution

  1. Step 1: Understand conditional mocking with matchers

    MockK supports argument matchers like gt() (greater than) and ltEq() (less than or equal) for conditions.
  2. Step 2: Evaluate options for correct matcher usage

    every { mock.process(gt(100)) } returns "Success" every { mock.process(ltEq(100)) } throws IllegalArgumentException() correctly uses gt(100) to return "Success" and ltEq(100) to throw exception, matching the requirement precisely.
  3. Final Answer:

    every { mock.process(gt(100)) } returns "Success" every { mock.process(ltEq(100)) } throws IllegalArgumentException() -> Option D
  4. Quick Check:

    Use gt() and ltEq() for conditional mocking [OK]
Hint: Use gt() and ltEq() matchers for numeric conditions [OK]
Common Mistakes:
  • Using match { } lambda instead of built-in matchers
  • Mocking only specific values, missing ranges
  • Returning success for all inputs ignoring conditions
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard