MockK är ett Kotlin-first ramverk för att skapa mock-objekt, utformat speciellt för Kotlin-ekosystemet med hänsyn till dess språkliga egenskaper: korutiner, tilläggsfunktioner, data class och sealed class. Till skillnad från Mockito, som har porterats till Kotlin från Java, utformades MockK från början för Kotlin-syntax och kräver inga ytterligare plugin för att fungera med final-klasser. Enligt uppgifter från MockK.io används biblioteket i mer än 40% av Kotlin-projekten med modultestning.
Huvudpunkter
MockK — är ett bibliotek för att skapa mock-objekt, skrivet i Kotlin och optimerat för dess syntax. Det löser samma uppgifter som Mockito — isolering av testad kod från beroenden — men gör det med hjälp av Kotlin-specifika konstruktioner: lambdor, DSL, reified generics och suspend-funktioner.
Den största fördelen med MockK jämfört med porterade lösningar — inbyggt stöd för Kotlin. I Mockito kräver mockning av final-klasser opt-in (mockito-inline) och statiska metoder mockStatic. MockK stöder detta som standard, eftersom Kotlin-klasser är som standard final, och att kringgå denna begränsning är inbyggt i bibliotekets arkitektur.
Version 1.13.12 (2024) — en stabil utgåva som stöder Kotlin 2.0, K2-kompilatorn och multiplattformsprojekt (KMP). MockK fungerar även med Kotlin/Native och Kotlin/JS, vilket gör det till det enda valet för KMP-projekt där varken Mockito eller EasyMock är tillämpliga.
MockK är utformat med hänsyn till Kotlins specifika egenskaper och använder språkets förmågor — reified generics, DSL med lambdor, inline-funktioner — för att tillhandahålla ett koncist och typsäkert API utan prestandaförlust.
Mekanismen MockK är baserad på bytekod-instrumentering genom biblioteket ByteBuddy (precis som Mockito), men slår in det i ett Kotlin-vänligt DSL. Istället för kedjor av when().thenReturn() använder MockK lambda-block every { } och coEvery { }, som ser ut som en naturlig utökning av språket. Under huven fångar MockK anropet inuti lambdan, analyserar metoden och argumenten genom reflektion och matchar dem mot registrerade stubbing-regler.
Blocket every { mock.method() } returns value läses som ”varje gång metoden anropas, returnera värdet”. En sådan deklarativ syntax ligger närmare Kotlin-stilen och eliminerar förvirring med argumentens ordning i when(). Tack vare reified generics i Kotlin härleds typen av mock automatiskt utan explicit klassangivelse.
val repository = mockk<UserRepository>()
// Stubbing: varje anrop av findById(1) returnerar användaren
every { repository.findById(1) } returns User("Alice")
// Anrop och verifiering
val result = repository.findById(1)
assertEquals("Alice", result.name)
Till skillnad från Mockito, där varje metod måste konfigureras explicit, stöder MockK relaxed mock — en mock som returnerar ”förnuftiga” standardvärden för vilken metod som helst: tom lista för List, 0 för Int, tom sträng för String. Detta minskar drastiskt mängden förberedande kod.
// Relaxed mock — alla metoder returnerar standardvärden
val api = mockk<ApiService>(relaxed = true)
// Kräver inte stubbing — returnerar tom lista
println(api.getUsers()) // []
MockK erbjuder flera sätt att skapa mock-objekt: mockk<T>() för en strikt mock (varje metod måste konfigureras explicit), mockk<T>(relaxed = true) för en avslappnad mock och spyk(obj) för att skapa en spy på ett verkligt objekt.
| Funktion | Typ | Beteende utan stubbing |
|---|---|---|
| mockk() | Strikt mock | Kastar undantag vid anrop av icke-stubbad metod |
| mockk(relaxed = true) | Avslappnad mock | Returnerar standardvärde |
| spyk() | Spy | Anropar verklig metod om ingen stub är konfigurerad |
| slot() | Argument Captor | Fångar argumentet för verifiering |
Valet mellan strikt och avslappnad mock beror på sammanhanget. En strikt mock garanterar att testet inte använder metoder vars beteende inte är definierat — detta ökar tillförlitligheten. En avslappnad mock är praktisk för snabb prototypning av tester där inte alla beroenden är viktiga. I praktiken rekommenderas att börja med en strikt mock och bara byta till relaxed när stubbing tar upp fler rader än själva testet.
Every-blocket — är den centrala konstruktionen för stubbing i MockK. Inuti lambdan beskrivs ett metodanrop med specifika argument, och sedan returneras värdet via returns, ett undantag kastas via throws eller ett svar beräknas via answers.
MockK stöder alla scenarier som behövs för testning: returnera värde, kasta undantag, beräkna svar baserat på argument, flera svar i ordning (sekvens av anrop).
// Returnera värde
every { repo.findById(1) } returns User("Alice")
// Kasta undantag
every { repo.findById(999) } throws NotFoundException()
// Dynamiskt svar
every { repo.save(any()) } answers {
val user = firstArg<User>()
user.copy(id = 42)
}
// Sekvens av svar
every { repo.findAll() } returnsMany listOf(
listOf(User("Alice")),
listOf(User("Bob")),
emptyList()
)
Verify i MockK är analogt med Mockito.verify() i betydelse men använder Kotlin DSL: verify { mock.method() }. För suspend-funktioner används coVerify { mock.suspendMethod() }, som fungerar korrekt med korutiner och inte kräver någon speciell runner.
MockK stöder samma modifierare som Mockito: exactly(1), atLeast(2), atMost(5), wasNot(Called). Syntaxen är minimalistisk — modifieraren skickas som första argument i verify { }.
// Verifiering: metoden anropades exakt 1 gång
verify(exactly = 1) { repo.save(any()) }
// Kontrollera ordning på anrop
verifySequence {
repo.save(any())
repo.flush()
}
// coVerify för suspend-funktioner
coVerify { api.fetchUsers() }
För att verifiera argument används slot() — analogen till ArgumentCaptor. Slot deklareras före anropet, skickas till every eller verify, och efter testets utförande innehåller det fångade värdet.
val userSlot = slot<User>()
verify { repo.save(capture(userSlot)) }
assertEquals("Alice", userSlot.captured.name)
MockK tillhandahåller annotationerna @MockK och @RelaxedMockK för att skapa mockar genom initiering i JUnit 5. Tillägget MockKExtension skapar automatiskt mockar före varje test och rensar efteråt — liknande MockitoExtension, men med stöd för relaxed-läge.
Annotationen @InjectMockKs (eller alternativet @MockK med explicit objektsskapande) injicerar mockar i den testade instansen. Detta minskar boilerplate och gör testkoden renare.
@ExtendWith(MockKExtension::class)
class UserServiceTest {
@MockK
lateinit var repository: UserRepository
@InjectMockKs
lateinit var service: UserService
@Test
fun `getUser returns user from repository`() {
every { repository.findById(1) } returns User("Alice")
assertEquals("Alice", service.getUser(1)?.name)
}
}
Valet mellan MockK och Mockito beror på teamets sammansättning och projektets typ. Mockito har ett större ekosystem, fler exempel och integrationer, men MockK erbjuder en renare Kotlin-syntax och inbyggt stöd för språkfunktioner. För nya Kotlin-projekt rekommenderas MockK som den mer idiomatiska lösningen.
| Kriterium | MockK | Mockito |
|---|---|---|
| Syntax | Kotlin DSL (every, verify) | Java-stil (when, thenReturn) |
| Korutiner | coEvery, coVerify (inbyggt) | Kräver extra bibliotek |
| Final class | Stöds som standard | Kräver mockito-inline |
| KMP | Stöds | Stöds inte |
| Relaxed mock | Inbyggt | Ingen motsvarighet |
| Popularitet | Växande i Kotlin-gemenskapen | Dominerar i Java- och hybridprojekt |
För projekt i ren Kotlin (utan Java-klasser) är MockK att föredra: mindre boilerplate, inbyggt stöd för korutiner, inga överraskningar med final-klasser. För hybridprojekt eller team med Java-bakgrund förblir Mockito ett fungerande alternativ — båda biblioteken kan användas i samma projekt genom olika moduler. Vid migrering från Mockito till MockK räcker det att byta ut @Mock-annotationer mot @MockK och skriva om when().thenReturn()-block till every { }-format.
Vanliga frågor
Relaxed mock returnerar standardvärden för alla icke-stubbade metoder (tom lista, 0, null), utan att kasta undantag. En vanlig (strict) mock kräver explicit stubbing av varje metod — annars misslyckas testet. Relaxed mock är praktisk för snabba tester, strict — för tillförlitliga tester.
MockK stöder mockning av tilläggsfunktioner genom mockkStatic(). Detta är möjligt eftersom tilläggsfunktioner i Kotlin är statiska metoder med en första mottagarparameter. För varje tilläggsfunktion måste klassen där den är deklarerad anges.
Ja, MockK stöder Kotlin Multiplatform (KMP) för gemensam kod. På plattformarna JVM, Native och JS kan det gemensamma API:et mockk(), every, verify användas. Detta gör MockK till det enda valet för KMP-projekt där Mockito inte fungerar.
Använd verifySequence { } — ett block där anrop anges strikt i förväntad ordning. Om den faktiska ordningen avviker, kommer verifySequence att kasta ett undantag med angivelse av första icke-överensstämmande anropet.
Ja, tekniskt är det möjligt, men rekommenderas inte. Konflikter kan uppstå på nivån av bytekod-instrumentering (ByteBuddy vs mockito-inline). Om projektet redan använder Mockito kan migreringen till MockK ske gradvis genom isolering av moduler.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också