MockK: τι είναι, βασικές έννοιες και σύνταξη

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-04-08 Χρόνος ανάγνωσης: 8 λεπ

Το MockK είναι ένα Kotlin-first πλαίσιο για τη δημιουργία mock-αντικειμένων, σχεδιασμένο ειδικά για το οικοσύστημα Kotlin λαμβάνοντας υπόψη τα γλωσσικά χαρακτηριστικά του: coroutine, συναρτήσεις επέκτασης, data class και sealed class. Σε αντίθεση με το Mockito, που είναι μεταφερμένο στο Kotlin από τη Java, το MockK είναι εξ αρχής σχεδιασμένο για τη σύνταξη Kotlin και δεν απαιτεί επιπλέον προσθέτες για την εργασία με final κλάσεις. Σύμφωνα με τα δεδομένα του MockK.io, η βιβλιοθήκη χρησιμοποιείται σε πάνω από 40% των έργων Kotlin με αρθρωματικό ελέγχο.

Βασικά σημεία

  • MockK — βιβλιοθήκη προσανατολισμένη στο Kotlin για mocking με υποστήριξη coroutine και γλωσσικών χαρακτηριστικών.
  • mockk() — η κύρια μέθοδος δημιουργίας ενός mock-αντικειμένου, ανάλογη με το Mockito.mock().
  • every { } — μπλοκ για ρύθμιση της συμπεριφοράς του mock (stubbing) σε δηλωτικό στυλ.
  • coEvery / coVerify — ειδικές κατασκευές για την εργασία με τις συναρτήσεις suspend των coroutine.
  • Relaxed mock — ένα mock που επιστρέφει προκαθορισμένες τιμές χωρίς ρητή stubbing.

Τι είναι το MockK;

MockK — είναι μια βιβλιοθήκη για τη δημιουργία mock-αντικειμένων, γραμμένη σε Kotlin και βελτιστοποιημένη για τη σύνταξή του. Λύνει τις ίδιες εργασίες με το Mockito — απομόνωση του ελεγχόμενου κώδικα από εξαρτήσεις — αλλά το κάνει χρησιμοποιώντας κατασκευές ειδικές για Kotlin: lambda, DSL, reified generics και συναρτήσεις suspend.

Το κύριο πλεονέκτημα του MockK έναντι των μεταφερμένων λύσεων — η εγγενής υποστήριξη του Kotlin. Στο Mockito, η πραξιμοποίηση final κλάσης απαιτεί opt-in (mockito-inline), και στατικών μεθόδων — mockStatic. Το MockK υποστηρίζει αυτό από προκαθορισμού, καθώς οι κλάσεις Kotlin είναι από προκαθορισμού final, και η παράκαμψη αυτού του περιορισμού είναι ενσωματωμένη στην αρχιτεκτονική της βιβλιοθήκης.

Έκδοση 1.13.12 (2024) — μια σταθερή έκδοση που υποστηρίζει Kotlin 2.0, τον μεταγλωττιστή K2 και πολυ-πλατφορμικά έργα (KMP). Το MockK λειτουργεί επίσης με Kotlin/Native και Kotlin/JS, καθιστώντας το τη μοναδική επιλογή για έργα KMP όπου ούτε το Mockito ούτε το EasyMock είναι εφαρμόσιμα.

Το MockK έχει σχεδιαστεί λαμβάνοντας υπόψη τις ιδιαιτερικότητες του Kotlin και χρησιμοποιεί τις δυνατότητες της γλώσσας — reified generics, DSL με lambda, συναρτήσεις inline — για να παρέχει ένα συντομο και ασφαλές από άποψη τύπων API χωρίς απώλεια απόδοσης.

Πώς λειτουργεί το MockK

Ο μηχανισμός του MockK βασίζεται σε προσαρμογή bytecode μέσω της βιβλιοθήκης ByteBuddy (όπως και το Mockito), αλλά το περιτυλίσσει σε ένα DSL φιλικό προς το Kotlin. Αντί για αλυσίδες when().thenReturn(), το MockK χρησιμοποιεί μπλοκ lambda every { } και coEvery { }, που μοιάζουν με φυσική επέκταση της γλώσσας. Κάτω από την επιφάνεια, το MockK παρακολουθεί την κλήση μέσα στο lambda, αναλύει τη μέθοδο και τα ορίσματα μέσω ανάκλασης και τα ταιριάζει με τους καταγεγραμμένους κανόνες stubbing.

Βασική σύνταξη MockK

Το μπλοκ every { mock.method() } returns value διαβάζεται ως “κάθε φορά που η μέθοδος καλείται, επίστρεψε την τιμή”. Μια τέτοια δηλωτική σύνταξη είναι πιο κοντά στο στυλ Kotlin και εξαλείφει τη σύγχυση με τη σειρά των ορισμάτων στο when(). Χάρη στα reified generics του Kotlin, ο τύπος του mock συμπεραίνεται αυτόματα χωρίς ρητή καθορισμό κλάσης.

kotlin
val repository = mockk<UserRepository>()

// Stubbing: κάθε κλήση findById(1) επιστρέφει τον χρήστη
every { repository.findById(1) } returns User("Alice")

// Κλήση και επαλήθευση
val result = repository.findById(1)
assertEquals("Alice", result.name)

Relaxed mock: λιγότερο βοηθητικό κώδικα

Σε αντίθεση με το Mockito, όπου κάθε μέθοδος πρέπει να ρυθμιστεί ρητά, το MockK υποστηρίζει relaxed mock — ένα mock που επιστρέφει “λογικές” προκαθορισμένες τιμές για οποιαδήποτε μέθοδο: κενή λίστα για List, 0 για Int, κενή συμβολοσειρά για String. Αυτό μειώνει δραστικά την ποσότητα του προετοιμαστικού κώδικα.

kotlin
// Relaxed mock — όλες οι μεθοδοι επιστρέφουν προκαθορισμένες τιμές
val api = mockk<ApiService>(relaxed = true)

// Δεν απαιτεί stubbing — θα επιστρέψει κενή λίστα
println(api.getUsers()) // []

Δημιουργία mock και relaxed mock

Το MockK προσφέρει αρκετούς τρόπους για τη δημιουργία mock-αντικειμένων: mockk<T>() για αυστηρό mock (κάθε μέθοδος πρέπει να ρυθμιστεί ρητά), mockk<T>(relaxed = true) για relaxed mock και spyk(obj) για τη δημιουργία spy σε ένα πραγματικό αντικείμενο.

ΣυνάρτησηΤύποςΣυμπεριφορά χωρίς stubbing
mockk()Αυστηρό mockΡίχνει εξαίρεση κατά την κλήση μη-stubbed μεθόδου
mockk(relaxed = true)Relaxed mockΕπιστρέφει προκαθορισμένη τιμή
spyk()SpyΚαλεί την πραγματική μέθοδο αν δεν έχει ρυθμιστεί stub
slot()Argument CaptorΣυλλαμβάνει το όρισμα για επαλήθευση

Η επιλογή μεταξύ αυστηρού και relaxed mock εξαρτάται από το πλαίσιο. Το αυστηρό mock εγγυάται ότι το τεστ δεν χρησιμοποιεί μεθόδους των οποίων η συμπεριφορά δεν είναι καθορισμένη — αυτό αυξάνει την αξιοπιστία. Το relaxed mock είναι βολικό για γρήγορη προτοσυποίηση δοκιμών όπου δεν είναι όλες οι εξαρτήσεις σημαντικές. Στην πρακτική, συνιστάται να ξεκινάτε με αυστηρό mock και να μεταβαίνετε σε relaxed μόνο όταν το stubbing καταλαμβάνει περισσότερες γραμμές από ότι η ίδια η δοκιμή.

Stubbing: ρύθμιση συμπεριφοράς με μπλοκ every

Το μπλοκ every — είναι η κεντρική κατασκευή stubbing στο MockK. Μέσα στο lambda, περιγράφεται μια κλήση μεθόδου με συγκεκριμένα ορίσματα, και στη συνέχεια η τιμή επιστρέφεται μέσω returns, η εξαίρεση ρίχνεται μέσω throws ή η απάντηση υπολογίζεται μέσω answers.

Διαφορετικοί τρόποι stubbing

Το MockK υποστηρίζει όλα τα σενάρια που απαιτούνται για δοκιμές: επιστροφή τιμής, ρίξιμο εξαίρεσης, υπολογισμός απάντησης βάσει ορισμάτων, πολλαπλές απαντήσεις σε σειρά (σειρά κλήσεων).

kotlin
// Επιστροφή τιμής
every { repo.findById(1) } returns User("Alice")

// Ρίξιμο εξαίρεσης
every { repo.findById(999) } throws NotFoundException()

// Δυναμική απάντηση
every { repo.save(any()) } answers {
    val user = firstArg<User>()
    user.copy(id = 42)
}

// Σειρά απαντήσεων
every { repo.findAll() } returnsMany listOf(
    listOf(User("Alice")),
    listOf(User("Bob")),
    emptyList()
)

Verify και coVerify για coroutine

Verify στο MockK είναι ανάλογο με το Mockito.verify() σημασιολογικά, αλλά χρησιμοποιεί DSL Kotlin: verify { mock.method() }. Για συναρτήσεις suspend χρησιμοποιείται το coVerify { mock.suspendMethod() }, που λειτουργεί σωστά με τα coroutine και δεν απαιτεί ειδικό εκτελεστή.

Έλεγχος αριθμού κλήσεων

Το MockK υποστηρίζει τους ίδιους τροποποιητές με το Mockito: exactly(1), atLeast(2), atMost(5), wasNot(Called). Η σύνταξη είναι μινιμαλιστική — ο τροποποιητής μεταβιβάζεται ως πρώτο όρισμα στο verify { }.

kotlin
// Έλεγχος: η μέθοδος κλήθηκε ακριβώς 1 φορά
verify(exactly = 1) { repo.save(any()) }

// Έλεγχος σειράς κλήσεων
verifySequence {
    repo.save(any())
    repo.flush()
}

// coVerify για συναρτήσεις suspend
coVerify { api.fetchUsers() }

Slot: σύλληψη ορισμάτων

Για τον έλεγχο ορισμάτων χρησιμοποιείται το slot() — ανάλογο του ArgumentCaptor. Το slot δηλώνεται πριν από την κλήση, μεταβιβάζεται στο every ή verify, και μετά την εκτέλεση του τεστ περιέχει τη συλληφθείσα τιμή.

kotlin
val userSlot = slot<User>()

verify { repo.save(capture(userSlot)) }

assertEquals("Alice", userSlot.captured.name)

Σχολιασμοί MockK και ενσωμάτωση JUnit

Το MockK παρέχει σχολιασμούς @MockK και @RelaxedMockK για τη δημιουργία mock μέσω αρχικοποίησης στο JUnit 5. Η επέκταση MockKExtension δημιουργεί αυτόματα mock πριν από κάθε δοκιμή και καθαρίζει μετά — ανάλογα με το MockitoExtension, αλλά με υποστήριξη της relaxed λειτουργίας.

Παράδειγμα με MockKExtension

Ο σχολιασμός @InjectMockKs (ή η εναλλακτική @MockK με ρητή δημιουργία αντικειμένου) ενχέει τα mock στο ελεγχόμενο στην δοκιμή. Αυτό μειώνει τον boilerplate κώδικα και κάνει τον κώδικα της δοκιμής πιο καθαρό.

kotlin
@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)
    }
}

MockK εναντίον Mockito: τι να διαλέξετε για Kotlin

Η επιλογή μεταξύ MockK και Mockito εξαρτάται από τη σύνθεση της ομάδας και τον τύπο του έργου. Το Mockito έχει μεγαλύτερο οικοσύστημα, περισσότερα παραδείγματα και ενσωματώσεις, αλλά το MockK προσφέρει πιο καθαρή σύνταξη Kotlin και εγγενή υποστήριξη για τα χαρακτηριστικά της γλώσσας. Για νέα έργα Kotlin, το MockK συνιστάται ως η πιο ιδιοματική λύση.

ΚριτήριοMockKMockito
ΣύνταξηKotlin DSL (every, verify)Στυλ Java (when, thenReturn)
CoroutinecoEvery, coVerify (εγγενή)Απαιτεί επιπλέον βιβλιοθήκες
Final classΥποστηρίζεται από προκαθορισμούΑπαιτεί mockito-inline
KMPΥποστηρίζεταιΔεν υποστηρίζεται
Relaxed mockΕνσωματωμένοΔεν υπάρχει αντίστοιχο
ΔημοφιλίαΑυξανόμενη στην κοινότητα KotlinΚυριαρχεί σε Java και υβριδικά έργα

Για έργα σε καθαρό Kotlin (χωρίς Java κλάσεις) το MockK είναι προτιμητέο: λιγότερος boilerplate, εγγενής υποστήριξη coroutine, χωρίς εκπλήξεις με final κλάσεις. Για υβριδικά έργα ή ομάδες με υποθάθρωση Java, το Mockito παραμένει μια λειτουργική επιλογή — και οι δύο βιβλιοθήκες μπορούν να χρησιμοποιηθούν σε ένα έργο μέσω διαφορετικών αρθρωμάτων. Κατά τη μετανάστευση από Mockito σε MockK, αρκεί να αντικαταστήσετε τις σχολιασμούς @Mock με @MockK και να επαναγράψετε τα μπλοκ when().thenReturn() σε μορφή every { }.

Συχνές ερωτήσεις

Ποια είναι η διαφορά μεταξύ relaxed mock και συνηθισμένου mock στο MockK;

Relaxed mock επιστρέφει προκαθορισμένες τιμές για όλες τις μη-stub μεθόδους (κενή λίστα, 0, null), χωρίς να ρίχνει εξαιρέσεις. Το συνηθισμένο (strict) mock απαιτεί ρητή stubbing κάθε μεθόδου — διαφορετικά το τεστ αποτυγχάνει. Το relaxed mock είναι βολικό για γρήγορα τεστ, το strict — για αξιόπιστα τεστ.

Πώς να κάνω mock σε συναρτήσεις επέκτασης στο MockK;

MockK υποστηρίζει την πραξιμοποίηση συναρτήσεων επέκτασης μέσω mockkStatic(). Αυτό είναι δυνατό διότι οι συναρτήσεις επέκτασης στο Kotlin είναι στατικές μεθοδοι με πρώτη παράμετρο τον δεκτη. Για κάθε συνάρτηση επέκτασης, πρέπει να καθοριστεί η κλάση στην οποία είναι δηλωμένη.

Λειτουργεί το MockK με Kotlin Multiplatform;

Ναι, το MockK υποστηρίζει Kotlin Multiplatform (KMP) για κοινό κώδικα. Στις πλατφόρμες JVM, Native και JS μπορείτε να χρησιμοποιήσετε το κοινό API mockk(), every, verify. Αυτό καθιστά το MockK τη μοναδική επιλογή για έργα KMP όπου το Mockito δεν λειτουργεί.

Πώς να ελέγξω τη σειρά κλήσεων στο MockK;

Χρησιμοποιήστε verifySequence { } — ένα μπλοκ όπου οι κλήσεις καθορίζονται αυστηρά στην αναμενόμενη σειρά. Εάν η πραγματική σειρά διαφέρει, το verifySequence θα ρίξει εξαίρεση με την ένδειξη της πρώτης μη συμφωνούσας κλήσης.

Μπορούν να χρησιμοποιηθούν το MockK και το Mockito μαζί σε ένα έργο;

Ναι, τεχνικά είναι δυνατό, αλλά δεν συνιστάται. Συγκρούσεις μπορούν να προκύψουν σε επίπεδο προσαρμογής bytecode (ByteBuddy vs mockito-inline). Εάν το έργο ήδη χρησιμοποιεί το Mockito, η μετανάστευση σε MockK μπορεί να γίνει σταδιακά με απομόνωση αρθρωμάτων.

Συμπεράσματα

  • MockK — βιβλιοθήκη Kotlin-first για mocking με εγγενή υποστήριξη γλώσσας.
  • every { } — δηλωτικό DSL για ρύθμιση συμπεριφοράς mock.
  • coEvery / coVerify — υποστήριξη coroutine suspend-συναρτήσεων χωρίς επιπλέον εξαρτήσεις.
  • Relaxed mock — mock με προκαθορισμένες τιμές που μειώνει τον boilerplate.
  • @MockK / @InjectMockKs — σχολιασμοί για αυτόματη δημιουργία mock σε JUnit 5.
  • MockK vs Mockito — το MockK είναι προτιμητέο για καθαρά έργα Kotlin και KMP.
  • verifySequence — έλεγχος αυστηρής σειράς κλήσεων μεθόδου.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης