Mock — vad är det, mock-objekt och bibliotek för tester

Författare: IT Sectr Publicerad: 2026-04-10 Lästid: 8 min

Mock — är ett ersättningsobjekt som imiterar beteendet hos en verklig komponent och gör det möjligt att verifiera interaktionen med den. Till skillnad från Stub som bara returnerar ett givet värde, registrerar Mock faktumet att en metod anropats, de skickade argumenten och antalet anrop. Enligt data från Mockito (2024) är Mock den mest populära typen av Test Double i Java och Kotlin-projekt, som används i mer än 70% av enhetstester för mobilapplikationer.

Huvudpunkter

  • Mock — objekt som verifierar interaktioner: vilka metoder anropades, med vilka argument och hur många gånger
  • Mockito — det mest populära biblioteket för att skapa Mock i Java och Android-projekt
  • MockK — alternativ till Mockito för Kotlin med inbyggt stöd för korutiner och sealed class
  • Behavior verification — den viktigaste skillnaden mellan Mock och Stub: Mock verifierar beteende, inte tillstånd
  • Over-mocking — den främsta Anti-Pattern: mock ska bara vara för externa beroenden

Vad är Mock?

Mock — är ett objekt som skapas av mocking-ramverket (Mockito, MockK, EasyMock), som imiterar ett gränssnitt eller en klass och registrerar alla anrop till sina metoder. Utvecklaren ställer in förväntningar: metod X kommer att anropas med argument Y och returnera Z. Efter att testet har körts kontrollerar Mock om förväntningarna överensstämmer med de faktiska anropen.

Termen kommer från teatermetaforen för Test Doubles: Mock är en ”imitator” som inte bara står på scenen (som Dummy), utan spelar en roll och verifierar om interaktionen med den var korrekt. Om den testade koden inte anropade metoden som Mock förväntade sig, eller anropade den med felaktiga argument — misslyckas testet med ett meddelande om en bruten förväntning.

Hur Mock fungerar

Mock skapas via ramverkets fabrik: mockk<MyInterface>() eller Mockito.mock(MyClass.java). Ramverket genererar ett proxy-objekt som fångar upp alla metodanrop. Varje anrop jämförs med förinställda förväntningar (expectations). Om anropet motsvarar förväntningen — returneras det angivna värdet. Om inte — returnerar Mock ett standardvärde eller kastar ett undantag, beroende på konfigurationen.

När är Mock nödvändigt

Mock är obligatoriskt när den testade koden interagerar med komponenter som har bieffekter: skicka data till servern, skriva till databasen, loggning, analys, navigering, visning av systemdialoger. Utan Mock kan dessa interaktioner inte verifieras utan att starta den verkliga infrastrukturen. Enligt Google Testing Blog är Mock det enda sättet att kontrollera att applikationen verkligen skickade en analysthändelse utan att starta en testserver.

Mock och Stub: detaljerad jämförelse

Skillnaden mellan Mock och Stub är ett av de mest diskuterade ämnena inom testning. Båda typerna ersätter det verkliga beroendet, men på fundamentalt olika sätt.

KriteriumMockStub
HuvudfrågaAnropades metoden?Vilket resultat returnerades?
VerifieringBeteende (verify)Tillstånd (assert)
DataåtergivningValfrittObligatoriskt
Exempelverify(analytics).logEvent("click")assertEquals(5, repository.getCount())
När användaBieffekterDataåtergivning

Praktisk regel: Mock eller inte

Ett enkelt test för att välja: fråga dig själv — ”om jag tar bort den här kodraden, kommer testet att misslyckas?”. Om testet verifierar returvärdet — behövs Stub (verifiering via assert). Om testet kontrollerar om koden anropade metoden med rätt argument — behövs Mock (verifiering via verify). Denna dikotomi följer av mönstret Command-Query Separation: metoder som ändrar tillstånd (commands) behöver Mock; metoder som returnerar data (queries) behöver Stub.

Mockito och MockK: jämförelse av bibliotek

Valet mellan Mockito och MockK är ett av de första besluten vid konfigurering av teststacken för ett Android-projekt i Kotlin. Båda biblioteken utför samma uppgift, men med olika syn på Kotlin-specifika funktioner.

Mockito: beprövad klassiker

Mockito — är de facto-standard för Java-projekt. Version 5.x stöder mock-objekt för final-klasser, statiska metoder och konstruktorer tack vare den inbyggda MockMaker. För Kotlin-projekt kräver Mockito ytterligare konfiguration: mockito-kotlin-tillägg för förbättrad syntax, mockito-inline för final-klasser. Mockito stöder inte Kotlin-korutiner och suspend-funktioner utan extra adaptrar.

MockK: Kotlin-first-strategi

MockK skapades specifikt för Kotlin. Det stöder inbyggt korutiner (coEvery, coVerify), sealed class, data class, object-singletoner och extension-funktioner. MockK:s syntax använder DSL med lambda-block, vilket ser naturligt ut i Kotlin-kod. MockK kan också mocka egenskaper (property mocking) utan extra konfiguration — detta är viktigt för Android-projekt som använder LiveData, StateFlow och Delegates.

kotlin
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)

// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user

Prestandajämförelse

Benchmarks (JVM Benchmark, 2024) visar att MockK skapar mock-objekt 15–20% snabbare än Mockito för Kotlin-projekt tack vare direkt arbete med Kotlin-bytecode, inte Java Reflections. För projekt med tusentals enhetstester kan skillnaden i byggtid vara märkbar: MockK sparar 30–60 sekunder vid fullständig testkörning i stora projekt.

Exempel på Mock-tester i Kotlin

Vi undersöker tre scenarier: testning av ViewModel med Mock-beroenden, testning av UseCase med verifiering av API-anrop och testning av korutiner med coVerify.

Exempel 1: ViewModel med mockad analys

kotlin
class ProfileViewModelTest {
    private val analytics = mockk<AnalyticsService>()
    private val repo = mockk<UserRepository>()
    private val vm = ProfileViewModel(repo, analytics)

    fun `profile opened logs analytics event`() {
        every { analytics.logEvent("profile_opened") } returns Unit

        vm.onViewCreated()

        verify { analytics.logEvent("profile_opened") }
    }
}

Exempel 2: UseCase med asynkron verifiering

kotlin
class SendMessageUseCaseTest {
    private val api = mockk<MessagingApi>()
    private val useCase = SendMessageUseCase(api)

    fun `send message with correct payload`() = runTest {
        val message = Message(text = "Hello", userId = 42)

        coEvery { api.sendMessage(any()) } returns MessageResult.Sent("msg_1")

        val result = useCase.execute(message)

        coVerify {
            api.sendMessage(match {
                it.text == "Hello" && it.userId == 42
            })
        }
        assertTrue(result is MessageResult.Sent)
    }
}

Exempel 3: argumentverifiering med ArgumentCaptor

kotlin
class OrderUseCaseTest {
    private val api = mockk<OrderApi>()
    private val useCase = OrderUseCase(api)
    private val slot = slot<OrderRequest>()

    fun `order request contains correct items`() = runTest {
        coEvery { api.placeOrder(capture(slot)) } returns OrderResult.Placed("order_1")

        useCase.execute(listOf("item_a", "item_b"))

        assertEquals(2, slot.captured.items.size)
        assertEquals("item_a", slot.captured.items[0])
    }
}

Bästa praxis för Mock-testning

Effektiv användning av Mock inom mobil utveckling kräver disciplin. Brott mot dessa regler förvandlar tester till bräckliga hinder som går sönder vid varje omfaktorisering.

Mocka endast applikationens externa gränser

Strikt regel: Mock skapas endast för beroenden som korsar applikationens gräns: API-klienter, databaser, filsystem, systemtjänster (LocationManager, BluetoothAdapter, Camera). Applikationens interna klasser — domänentiteter, Value Object, enkla verktyg — bör inte ersättas med Mock. Deras beteende testas via verkliga objekt.

En assert/verify per test

Varje test bör innehålla exakt en logisk kontroll — antingen verify (för Mock), eller assert (för Stub). Blanda inte verifiering av tillstånd och beteende i ett test. Om både API-anropet och resultatet måste kontrolleras — skapa två separata tester med olika namn. Denna regel, känd som ”en assert per test”, härstammar från Kent Becks rekommendationer (2002).

  • Använd relaxUnitFun = true i MockK för metoder som returnerar Unit — annars kastar Mock ett undantag vid ett obeskrivet anrop
  • Begränsa verify till endast kritiska anrop — verifiera inte varje getter och setter, detta gör tester bräckliga
  • Tillämpa ArgumentMatchers meningsfullt — any() döljer viktiga detaljer om argumentet är avgörande för affärslogiken
  • Missbruka inte verifyNoMoreInteractions — denna metod gör testet onödigt stelt inför alla ändringar i produktionskoden
  • Använd @MockkAnnotations för automatisk initiering av Mock-objekt — detta minskar boilerplate och förbättrar läsbarheten

Avancerade tekniker för Mock-testning

Förutom grundläggande mockning finns det avancerade tekniker som löser specifika uppgifter inom mobil utveckling: testning av flertrådning, verifiering av Flow-tillstånd och partiell mockning av verkliga objekt.

Partiell Mock med spyK

Spy (eller partiell mock) gör det möjligt att skapa ett objekt som delegerar anrop till den verkliga implementeringen, men tillåter överskridning av enskilda metoder. I MockK skapas spyk baserat på en verklig instans av klassen: val repo = spyk(InMemoryUserRepository()). Anrop för vilka förväntningar har satts via every går genom Mock; resten — genom det verkliga objektet. Spy är särskilt användbart för testning av äldre kod där beroendeinjektion ännu inte har implementerats, och endast en metod behöver åsidosättas.

Testning av StateFlow med Turbine

I moderna Android-projekt med Jetpack Compose exponerar ViewModel tillstånd via StateFlow. MockK gör det möjligt att mocka Flow-beroenden, och Turbine-biblioteket förenklar verifiering av emission. Det klassiska mönstret: MockK för UseCase som returnerar Flow, Turbine för verifiering av ViewModel-emission. Denna stack rekommenderas av dokumentationen för Android Testing (Google, 2024) för projekt på Kotlin Coroutines.

kotlin
class SearchViewModelTest {
    private val searchUseCase = mockk<SearchUseCase>()
    private val vm = SearchViewModel(searchUseCase)

    fun `search emits results`() = runTest {
        coEvery { searchUseCase.search("android") } returns
            flowOf(SearchResult.Success(listOf(Item("Android TDD"))))

        vm.search("android")

        vm.state.test {
            val state = awaitItem()
            assertTrue(state.items.isNotEmpty())
            cancelAndIgnoreRemainingEvents()
        }
    }
}

Vanliga frågor

Vad är skillnaden mellan Mock och Mockito?

Mock — är ett koncept, en typ av Test Double som verifierar beteende. Mockito — är ett bibliotek för att skapa Mock-objekt i Java och Android. Andra bibliotek: MockK (Kotlin), EasyMock (Java), Cuckoo (iOS).

Hur fungerar Mock med Kotlin-korutiner?

För att testa suspend-funktioner med Mock, använd MockK (coEvery / coVerify) eller Mockito med mockito-kotlin. MockK stöder korutiner inbyggt: coEvery definierar beteendet för suspend-funktionen, coVerify verifierar dess anrop inuti korutinen. Alla suspend-anrop måste utföras inuti runTest (kotlinx-coroutines-test).

Kan Mock returnera olika värden vid upprepade anrop?

Ja. I MockK används returnsMany för detta: every { api.getData() } returnsMany listOf(response1, response2). I Mockito — kedjan thenReturn(value1).thenReturn(value2). Detta är användbart för att testa beteende vid sekventiella anrop med olika svar.

Hur rensar man Mock-tillstånd mellan tester?

I MockK använd annoteringen @MockK med fältet relaxed = true och anropa clearMocks(mock) i @After-metoden. I MockitoMockito.reset(mock). Bästa praxis: skapa en ny Mock för varje test via @Before för att utesluta påverkan mellan tester.

Hur hanterar Mock sealed class i Kotlin?

MockK fungerar korrekt med sealed class: every { useCase() } returns Result.Success(data). Mockito stöder inte sealed class direkt, utan kräver kringgångar. Detta är en av anledningarna till varför MockK rekommenderas för Kotlin-projekt istället för Mockito.

Sammanfattning

  • Mock — typ av Test Double som verifierar beteende (verify), inte tillstånd (assert) hos beroenden
  • Mockito — standard för Java/Android, MockK — Kotlin-first-val med stöd för korutiner och sealed class
  • Huvudregel: Mock för externa gränser (nätverk, DB, systemtjänster), verkliga objekt för interna klasser
  • Over-mocking — den främsta Anti-Pattern: överdriven ersättning av beroenden gör tester bräckliga och mindre användbara
  • Ett test — en logisk kontroll: verify för Mock eller assert för Stub, men inte båda i ett test
  • ArgumentCaptor / slot — korrekt sätt att verifiera Mock-anropsargument istället för blind any()
  • MockK rekommenderas för Kotlin-projekt: coEvery och coVerify fungerar inbyggt med korutiner utan extra adaptrar

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.

Diskutera projektet

Läs också