Blog Infos
Author
Published
Topics
, , , ,
Published

Hey again! Now that you’re comfortable with the basics of MockK, let’s level up and explore some advanced features. We’ll cover things like:

  • Mocking static methods, objects, and final classes.
  • Using mockkStaticmockkObject, and mockkClass.
  • Advanced constructs like slot and capture to inspect method arguments.

This is where MockK really shines compared to other libraries like Mockito. Ready? Let’s dive in!

Mocking Static Methods

Sometimes, you need to mock static methods — those pesky functions tied directly to a class, not an instance. In MockK, you can use mockkStatic to achieve this.

Example of mockkStatic

Let’s say you have a static method in a utility class:

object Utils {
    fun getCurrentTime(): Long = System.currentTimeMillis()
}

 

In a test, you don’t want to rely on the real system time. Instead, you can mock getCurrentTime() to return a fixed value:

import io.mockk.every
import io.mockk.mockkStatic
import org.junit.Assert.assertEquals
import org.junit.Test
class UtilsTest {
    @Test
    fun `test getCurrentTime returns mocked value`() {
        // Mock the static method
        mockkStatic(Utils::class)
        every { Utils.getCurrentTime() } returns 1234567890L
        // Call the method
        val result = Utils.getCurrentTime()
        // Verify the result
        assertEquals(1234567890L, result)
    }
}

 

Key Points
  • Remember to call mockkStatic() before stubbing the static method.
  • You can reset the mock with unmockkStatic(Utils::class) if needed.
Mocking Objects

Sometimes, you need to mock a singleton object in your code. MockK makes this easy with mockkObject.

Example of mockkObject

Let’s say you have a singleton object:

object ConfigManager {
    fun getApiUrl(): String = "https://real-api.com"
}

 

To mock this object during testing:

import io.mockk.every
import io.mockk.mockkObject
import org.junit.Assert.assertEquals
import org.junit.Test

class ConfigManagerTest {
    @Test
    fun `test getApiUrl returns mocked value`() {
        // Mock the singleton object
        mockkObject(ConfigManager)
        every { ConfigManager.getApiUrl() } returns "https://mock-api.com"
        // Call the method
        val result = ConfigManager.getApiUrl()
        // Verify the result
        assertEquals("https://mock-api.com", result)
    }
}

 

Key Points
  • Use mockkObject for mocking singleton objects.
  • Reset the mock with unmockkObject(ConfigManager) if needed.
Mocking Final Classes

In Kotlin, all classes are final by default, meaning you can’t subclass them. Some mocking libraries struggle with this, but MockK handles it easily with mockkClass.

Example of mockkClass

Let’s say you have a final class:

class UserRepository {
    fun getUser(id: Int): String = "Real User"
}

 

To mock this final class in your tests:

import io.mockk.every
import io.mockk.mockkClass
import org.junit.Assert.assertEquals
import org.junit.Test
class UserRepositoryTest {
    @Test
    fun `test getUser returns mocked value`() {
        // Create a mock of the final class
        val mockUserRepository = mockkClass(UserRepository::class)
        every { mockUserRepository.getUser(1) } returns "Mocked User"
        // Call the method
        val result = mockUserRepository.getUser(1)
        // Verify the result
        assertEquals("Mocked User", result)
    }
}

 

Key Points
  • mockkClass works with final classes, making MockK perfect for Kotlin.
  • Reset the mock with unmockkAll() if needed.
Advanced Constructs: slot and capture

Sometimes, you need to capture arguments passed to a mocked function and inspect them later. MockK provides slot and capture for this purpose.

What is a slot?

slot is a placeholder where you can store an argument passed to a mocked function.

Example of Using slot

Let’s say you have a function that sends a user ID to an API:

class ApiService {
    fun sendUserId(userId: Int) {
        // Sends the user ID to an API
    }
}

 

You want to verify that the correct user ID is sent. Here’s how you can capture the argument:

import io.mockk.CapturingSlot
import io.mockk.every
import io.mockk.just
import io.mockk.runs
import io.mockk.slot
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Test
class ApiServiceTest {
    @Test
    fun `test sendUserId captures the correct user ID`() {
        val apiService = mockk<ApiService>()
        val userIdSlot = slot<Int>()
        // Stub the method to capture the argument
        every { apiService.sendUserId(capture(userIdSlot)) } just runs
        // Call the method
        apiService.sendUserId(42)
        // Verify the captured value
        assertEquals(42, userIdSlot.captured)
    }
}

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Intro to unit testing coroutines with Kotest & MockK

In this workshop, you’ll learn how to test coroutines effectively using the Kotest and MockK libraries, ensuring your app handles concurrent tasks efficiently and with confidence.
Watch Video

Intro to unit testing coroutines with Kotest & MockK

Jaroslaw Michalik
Kotlin GDE

Intro to unit testing coroutines with Kotest & MockK

Jaroslaw Michalik
Kotlin GDE

Intro to unit testing coroutines with Kotest & MockK

Jaroslaw Michali ...
Kotlin GDE

Jobs

No results found.

Key Points
  • Use capture(userIdSlot) to store the argument passed to the method.
  • You can inspect the captured value with userIdSlot.captured.
Unit Testing Android Components with MockK

MockK can also help you test Android components like ViewModelLiveDataActivity, and Fragment. Here’s a quick overview.

Mocking a ViewModel

Let’s say you have a UserViewModel that fetches user data:

class UserViewModel(private val userRepository: UserRepository) : ViewModel() {
    val userName = MutableLiveData<String>()
    fun loadUser(userId: Int) {
        val name = userRepository.getUser(userId)
        userName.value = name
    }
}

 

Testing the ViewModel with MockK
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Observer
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test

class UserViewModelTest {
    @get:Rule
    val rule = InstantTaskExecutorRule()
    @Test
    fun `test loadUser updates userName LiveData`() {
        val mockUserRepository = mockk<UserRepository>()
        val viewModel = UserViewModel(mockUserRepository)
        val observer = mockk<Observer<String>>(relaxed = true)
        // Stub the repository
        every { mockUserRepository.getUser(1) } returns "John Doe"
        // Observe the LiveData
        viewModel.userName.observeForever(observer)
        // Call the function
        viewModel.loadUser(1)
        // Verify the LiveData was updated
        verify { observer.onChanged("John Doe") }
    }
}

 

Key Points
  • Use InstantTaskExecutorRule to test LiveData.
  • Use mockk<Observer<T>>() to mock LiveData observers.
Conclusion

Congratulations! You’ve learned how to:

  • Mock static methods with mockkStatic.
  • Mock singleton objects with mockkObject.
  • Mock final classes with mockkClass.
  • Use advanced constructs like slot and capture.

In the next article, we’ll dive into testing asynchronous code with MockK, including coroutines and Flows. Stay tuned, and happy testing!

This article is previously published on proandroiddev.com.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
Using annotations in Kotlin has some nuances that are useful to know
READ MORE
blog
One of the latest trends in UI design is blurring the background content behind the foreground elements. This creates a sense of depth, transparency, and focus,…
READ MORE
blog
Now that Android Studio Iguana is out and stable, I wanted to write about…
READ MORE
blog
The suspension capability is the most essential feature upon which all other Kotlin Coroutines…
READ MORE
Menu