agentsclimarketplace

Kotlin testing

Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/kotlin-testing

When to activate: Kotlin testing, JUnit 5, Kotest, MockK, Testcontainers, coroutine testing, Spring test slices, runTestFrom its SKILL.md

Install
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill kotlin-testing

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.9 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it

Kotlin Testing Patterns

JUnit 5 + MockK

@ExtendWith(MockKExtension::class)
class UserServiceTest {

    @MockK lateinit var userRepository: UserRepository
    @MockK lateinit var emailService: EmailService
    @InjectMockKs lateinit var userService: UserService

    @Test
    fun `findById returns user when exists`() {
        val user = User(id = 1L, name = "Alice", email = "[email protected]")
        every { userRepository.findById(1L) } returns Optional.of(user)

        val result = userService.findById(1L)

        assertThat(result).isEqualTo(user.toDto())
        verify(exactly = 1) { userRepository.findById(1L) }
    }

    @Test
    fun `findById throws when not found`() {
        every { userRepository.findById(any()) } returns Optional.empty()

        assertThrows<ResourceNotFoundException> { userService.findById(99L) }
    }

    @Test
    fun `create sends welcome email`() {
        val request = CreateUserRequest("Bob", "[email protected]")
        every { userRepository.existsByEmail(any()) } returns false
        every { userRepository.save(any()) } answers { firstArg() }
        every { emailService.sendWelcome(any()) } just Runs

        userService.create(request)

        verify { emailService.sendWelcome(match { it.email == "[email protected]" }) }
    }
}

Kotest

class UserServiceSpec : BehaviorSpec({
    val repository = mockk<UserRepository>()
    val service = UserService(repository)

    Given("a user exists") {
        val user = User(1L, "Alice", "[email protected]")
        every { repository.findById(1L) } returns Optional.of(user)

        When("findById is called") {
            val result = service.findById(1L)

            Then("returns the user DTO") {
                result shouldBe user.toDto()
            }
        }
    }
})

// Data-driven tests
class EmailValidationSpec : FunSpec({
    forAll(
        row("[email protected]", true),
        row("invalid-email", false),
        row("", false),
        row("missing@domain", false),
    ) { email, expected ->
        test("$email is ${if (expected) "valid" else "invalid"}") {
            email.isValidEmail() shouldBe expected
        }
    }
})

Coroutine Testing

class FlowViewModelTest {
    @get:Rule val mainDispatcherRule = MainDispatcherRule()

    @Test
    fun `search results update after debounce`() = runTest {
        val fakeRepo = FakeSearchRepository()
        val vm = SearchViewModel(fakeRepo)

        vm.onQueryChange("kotlin")
        advanceTimeBy(400) // past 300ms debounce

        assertEquals(fakeRepo.expectedResults, vm.results.value)
    }

    @Test
    fun `stateflow collects values`() = runTest {
        val flow = MutableStateFlow(0)
        val collected = mutableListOf<Int>()

        val job = launch { flow.collect { collected.add(it) } }
        flow.value = 1
        flow.value = 2
        job.cancel()

        assertThat(collected).containsExactly(0, 1, 2)
    }
}

Testcontainers

@SpringBootTest
@Testcontainers
class UserRepositoryIntegrationTest {

    companion object {
        @Container @JvmStatic
        val postgres = PostgreSQLContainer("postgres:16-alpine")
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test")

        @DynamicPropertySource @JvmStatic
        fun configureProperties(registry: DynamicPropertyRegistry) {
            registry.add("spring.datasource.url", postgres::getJdbcUrl)
            registry.add("spring.datasource.username", postgres::getUsername)
            registry.add("spring.datasource.password", postgres::getPassword)
        }
    }

    @Autowired lateinit var repository: UserRepository

    @Test
    @Transactional
    fun `save and find user`() {
        val user = User(name = "Test", email = "[email protected]")
        val saved = repository.save(user)
        val found = repository.findById(saved.id!!)
        assertThat(found).isPresent.get().extracting("email").isEqualTo("[email protected]")
    }
}

Spring Test Slices

// Only loads web layer
@WebMvcTest(UserController::class)
class UserControllerTest {
    @Autowired lateinit var mvc: MockMvc
    @MockBean lateinit var userService: UserService

    @Test
    fun `GET user returns 200`() {
        every { userService.findById(1L) } returns UserResponse(1L, "Alice")

        mvc.get("/api/v1/users/1")
            .andExpect { status { isOk() } }
            .andExpect { jsonPath("$.name") { value("Alice") } }
    }
}

// Only loads data layer
@DataJpaTest
class OrderRepositoryTest {
    @Autowired lateinit var repository: OrderRepository
    @Autowired lateinit var entityManager: TestEntityManager
    // ...
}

AssertJ Custom Assertions

fun assertThatUser(user: User) = UserAssert(user)

class UserAssert(actual: User) : AbstractAssert<UserAssert, User>(actual, UserAssert::class.java) {
    fun isActive() = apply { check("status") { actual.status == UserStatus.ACTIVE } }
    fun hasEmail(email: String) = apply { check("email") { actual.email == email } }
}

// Usage
assertThatUser(user).isActive().hasEmail("[email protected]")

Key Rules

  • Use mockk over Mockito — idiomatic Kotlin, no any() casting issues, supports object mocking
  • Use runTest for coroutine tests — it uses TestCoroutineScheduler and auto-advances virtual time
  • @Testcontainers with @Container static field — reuse containers across tests with companion object
  • Spring slices (@WebMvcTest, @DataJpaTest) are faster than full @SpringBootTest — use them for focused tests
  • Prefer every { } returns over whenever { } thenReturn — cleaner in Kotlin

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.