
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
mockkStatic,mockkObject, andmockkClass. - Advanced constructs like
slotandcaptureto 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
mockkObjectfor 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
mockkClassworks 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?
A 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
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 ViewModel, LiveData, Activity, 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
InstantTaskExecutorRuleto 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
slotandcapture.
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.



