MockK Beginner Tutorial
Understanding Mocking and MockK
- Mocking: In unit testing, mocking involves creating simulated objects (mocks) that mimic the behavior of real dependencies. This allows you to isolate the unit under test (your code) from external components and test its functionality independently.
- MockK: MockK is a lightweight, concise mocking library specifically designed for Kotlin. It offers a fluent API for creating mocks, defining their behavior, and verifying interactions with them during tests.
Prerequisites
- Basic understanding of unit testing in Android with Kotlin
- Android Studio or an IDE of your choice
Setting Up MockK
Add Dependency: Add the MockK library to your app
module's build.gradle
file:
dependencies { testImplementation 'io.mockk:mockk:VERSION'
androidTestImplementation 'io.mockk:mockk-android:VERSION' }
Replace VERSION
with the latest MockK version (check https://github.com/mockk/mockk).
Creating Mocks
mockk
function: Use the mockk
function to create a mock object of a specific class:
val mockRepository = mockk<MyRepository>()
Relaxed vs. Strict Mocks:
- Relaxed (default): Relaxed mocks return default values (null, 0, etc.) for unstubbed methods. They’re suitable for most use cases.
- Strict: Strict mocks throw exceptions for unstubbed methods, helping ensure you define all expected behavior. Use
mockk(strict = true)
for strict mode.
Defining Mock Behavior
every
block: Use theevery
block to define how a mock should behave when its methods are called:
every { mockRepository.fetchData() } returns listOf(User("Alice"), User("Bob"))
This configures the mockRepository
's fetchData
method to always return a list of two users.
Verifying Mock Interactions
verify
block: Use theverify
block to assert that certain methods were called on a mock during your test:
verify { mockRepository.fetchData() wasCalled }
- This verifies that
fetchData
was called at least once on themockRepository
mock.
Key MockK Features
- Argument Capturing: Capture arguments passed to mock methods using
capture
:
val capturedId = capture<Long>()
every { mockRepository.getById(capture(capturedId)) } returns User("Charlie")
// Later in your test verify
{ mockRepository.getById(10L) } // Assert that 10L was captured
- Spying: Create a spy on an existing object using
spyk
:
val realObject = MyClass()
val spyObject = spyk(realObject)
- The spy allows you to observe real object behavior while also mocking specific methods.
- Matching Arguments: Utilize advanced argument matching for complex scenarios using matchers provided by MockK or writing custom matchers. Refer to the MockK documentation for details.
Example: Unit Testing a ViewModel with MockK
Consider a simple ViewModel that fetches data from a repository:
Kotlin
class MyViewModel(private val repository: MyRepository) {
fun getUserList() {
val users = repository.fetchData()
// Update UI or perform other actions with users
}
}
Test with MockK:
Kotlin
class MyViewModelTest {
@get:Rule
val rule = InstantTaskExecutorRule() // Optional for LiveData testing if using LiveData
@Test
fun testUserListFetch() {
val mockRepository = mockk<MyRepository>()
every { mockRepository.fetchData() } returns listOf(User("David"), User("Eve"))
val viewModel = MyViewModel(mockRepository)
viewModel.getUserList() // Trigger the data fetch
verify { mockRepository.fetchData() wasCalled } // Assert data fetch was called
}
}
Additional Considerations
- AndroidX Test Runner: MockK supports both JUnit and AndroidX Test Runner. Adjust your test class accordingly.
- Advanced Mocking Techniques: MockK offers a rich set of features for more complex scenarios. Explore the documentation for details.