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.

Gives 0 of the 12 instructions most test skills give in ~1.3k tokens

Counted across 964 of the 1,571 authors here whose files we hold, read 2026-08-07

  • Close the browser when donein 55 of 964, across 12 files
  • Wait for network idle statein 51 of 964, across 6 files
  • Launch Chromium in headless modein 49 of 964, across 6 files
  • Use descriptive selectors for elementsin 49 of 964, across 6 files
  • Run provided scripts with help flag firstin 49 of 964, across 6 files
  • Add appropriate explicit waitsin 48 of 964, across 5 files
  • Use bundled scripts as black boxesin 46 of 964, across 3 files
  • Do not read script source codein 46 of 964, across 3 files
  • Use sync playwright for scriptsin 46 of 964, across 3 files
  • Inspect dom before executing actionsin 46 of 964, across 3 files
  • Run the full test suitein 37 of 964
  • Write the failing test firstin 29 of 964, across 23 files

Said here and by no other author read

  • use MockK over Mockito
  • use runTest for coroutine tests
  • use Spring test slices for focused tests
  • prefer every returns over whenever thenReturn

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 326,758. 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.