Blog Infos
Author
Published
Topics
, , , ,
Published

. . .

Hey there! In this article, we’ll discuss best practices for writing tests that are maintainable, readable, and efficient when using MockK. We’ll also cover tips to avoid common pitfalls and look at some performance considerations and alternatives to MockK.

Let’s dive in! 🚀

 

Writing Maintainable and Readable Tests
1. Follow the Arrange-Act-Assert (AAA) Pattern

Structuring your tests with the Arrange-Act-Assert pattern makes them easy to read and understand:

  1. Arrange: Set up your mocks and data.
  2. Act: Call the function under test.
  3. Assert: Verify the results and interactions.
Example:

 

@Test
fun `loadUser updates userName LiveData`() = runTest {
    // Arrange
    val mockRepository = mockk<UserRepository>()
    val viewModel = UserViewModel(mockRepository)
    coEvery { mockRepository.getUser(1) } returns "John Doe"
    // Act
    viewModel.loadUser(1)
    // Assert
    coVerify { mockRepository.getUser(1) }
    assertEquals("John Doe", viewModel.userName.value)
}

 

2. Use Descriptive Test Names

Your test names should clearly explain what behavior is being tested. A good format is:

[Function or Scenario] [Expected Behavior] [Condition (if applicable)]
Examples:
  • loadUser updates userName LiveData when repository returns data
  • saveUser throws exception when repository fails
3. Use relaxed and mockk(relaxed = true) for Simplicity

If you only care about interactions (not return values), use relaxed mocks to avoid specifying stubs unnecessarily:

val mockRepository = mockk<UserRepository>(relaxed = true)

This simplifies setup because MockK will provide default values for functions.

4. Add Comments for Complex Tests

When dealing with complex logic, add comments to explain why the test is structured a certain way.

@Test
fun `loadUser handles error gracefully`() = runTest {
    // Arrange: Simulate an exception when fetching the user
    val mockRepository = mockk<UserRepository>()
    coEvery { mockRepository.getUser(any()) } throws Exception("Network error")
    // Act: Call the function under test
    viewModel.loadUser(1)
    // Assert: Verify error handling logic
    assertEquals("Error", viewModel.errorMessage.value)
}
5. Keep Tests Focused on One Behavior

Each test should verify a single piece of behavior. Avoid testing multiple scenarios in one test case.

. . .
Tips to Avoid Common Issues with MockK
1. Mock Only What You Own

Avoid mocking classes you don’t control, such as third-party libraries or Android framework classes. Instead, use real instances or abstractions (interfaces) for these dependencies.

2. Verify Behavior, Not Implementation

Focus on verifying what the code does rather than how it does it. This makes your tests more resilient to refactoring.

✅ Good Verification:

coVerify { repository.getUser(1) }

❌ Bad Verification (testing implementation details):

coVerify { repository.internalCache.clear() }
3. Use clearMocks() to Avoid State Leaks

Mocks can retain state between tests. Use clearMocks() to reset them after each test:

@After
fun tearDown() {
    clearMocks(mockRepository)
}
4. Handle LiveData Properly

Use InstantTaskExecutorRule to execute LiveData updates synchronously in tests:

@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
5. Be Careful with mockkStatic and mockkObject

Mocking static methods or objects can cause issues if not properly cleaned up. Always unmock after the test:

mockkStatic("com.example.Utils")
// Test code...
unmockkStatic("com.example.Utils")

 

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.

Performance Considerations
1. Avoid Overuse of Mocks

Mocks are powerful but come with a performance cost. Whenever possible, use real implementations or fake objects for dependencies that are lightweight.

Example: Using a Fake Repository

 

class FakeUserRepository : UserRepository {
    override suspend fun getUser(id: Int): String = "Fake User"
}

 

This can speed up your tests compared to using complex mocks.

2. Minimize Use of mockkStatic and mockkObject

Mocking static methods or objects is expensive and can slow down your test suite. Use it sparingly and only when necessary.

3. Parallelize Tests

If your test suite is large, consider running tests in parallel to reduce execution time. However, ensure your tests are isolated and don’t share mutable state.

4. Profile Your Tests

Use tools like Android Studio’s Profiler to identify slow tests and bottlenecks. Optimize or refactor slow tests as needed.

Alternatives to MockK
1. Mockito

Mockito is another popular mocking framework for Java and Kotlin. It’s simpler but doesn’t support some of MockK’s advanced features like mocking final classes or top-level functions.

Pros:
  • Widely used and well-documented.
  • Easy to integrate with Android projects.
Cons:
  • Requires additional libraries (e.g., mockito-inline) to mock final classes.
  • Slightly less feature-rich compared to MockK for Kotlin.
2. Fake Objects

For simple dependencies, fakes can be more efficient than mocks. Fakes are real implementations with simplified logic.

Example:

 

class FakeUserRepository : UserRepository {
    override suspend fun getUser(id: Int): String = "Fake User"
}

 

3. Kotlinx.coroutines Test Utilities

For coroutine-based code, kotlinx.coroutines test utilities can simplify testing without mocks:

  • TestCoroutineDispatcher
  • runTest
  • TestScope
Conclusion

In this article, we covered:

  • Best practices for writing maintainable and readable tests with MockK.
  • Tips to avoid common pitfalls like state leaks and over-mocking.
  • Performance considerations and alternatives to MockK.

By following these practices, you’ll write tests that are clean, efficient, and resilient to changes in your code. Happy testing! 🧪🚀

This article is previously published on proandroiddev.com.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE
Menu