Test Doubles — τι είναι, είδη υποκατάστατων και χρήση

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

Test Doubles — αντικείμενα υποκατάστασης που χρησιμοποιούνται στο unit testing αντί για πραγματικές εξαρτήσεις. Ο όρος εισήχθη από τον Gerard Meszaros στο βιβλίο "xUnit Test Patterns" (2007) ως γενικός όρος για τα Mock, Stub, Fake, Spy και Dummy. Σύμφωνα με τα δεδομένα του Martin Fowler (2024), τα Test Doubles επιτρέπουν την απομόνωση του εξεταζόμενου στοιχείου από το περιβάλλον του, καθιστώντας τα τεστ ντετερμινιστικά, γρήγορα και ανεξάρτητα από εξωτερικές υπηρεσίες.

Τα κύρια σημεία

  • Test Doubles — γενικός όρος για όλους τους τύπους αντικειμένων υποκατάστασης στον έλεγχο
  • Mock ελέγχει την αλληλεπίδραση: ποιες μέθοδοι κλήθηκαν και με ποια ορίσματα
  • Stub επιστρέφει προκαθορισμένες τιμές χωρίς έλεγχο των κλήσεων
  • Fake — απλοποιημένη λειτουργική υλοποίηση (π.χ. in-memory βάση δεδομένων)
  • Spy καταγράφει τις κλήσεις για μεταγενέστερη επαλήθευση, Dummy συμπληρώνει τις παραμέτρους

Τι είναι τα Test Doubles;

Test Doubles — όρος από την αυτοκινητοβιομηχανία (κασκαντέρ, "double" για ηθοποιούς), μεταφερμένος στην ανάπτυξη λογισμικού. Όπως ο κασκαντέρ αντικαθιστά τον ηθοποιό σε μια επικίνδυνη σκηνή, το Test Double αντικαθιστά το πραγματικό στοιχείο σε ένα σενάριο ελέγχου. Αυτό είναι απαραίτητο όταν η πραγματική εξάρτηση δεν είναι διαθέσιμη, είναι αργή, μη ντετερμινιστική ή έχει παρενέργειες.

Η έννοια Test Double γενικεύει πέντε συγκεκριμένους τύπους, καθένας από τους οποίους λύνει το δικό του πρόβλημα. Η τυπολογία του Meszaros είναι κανονική και χρησιμοποιείται σε όλους τους σύγχρονους οδηγούς ελέγχου. Η διαφορά μεταξύ των τύπων έγκειται στον βαθμό ελέγχου και επαλήθευσης: από την απλή συμπλήρωση παραμέτρων (Dummy) έως τον πλήρη έλεγχο της σειράς των κλήσεων (Mock).

Γιατί χρειάζονται τα Test Doubles

Ο κύριος σκοπός των Test Doubles — η απομόνωση του εξεταζόμενου module. Στην ανάπτυξη εφαρμογών για κινητά, πραγματικές εξαρτήσεις είναι οι API διακομιστές, οι βάσεις δεδομένων, το σύστημα αρχείων, οι αισθητήρες της συσκευής, οι υπηρεσίες συστήματος (LocationManager, Camera, Bluetooth). Η άμεση χρήση αυτών των στοιχείων καθιστά τα τεστ αργά, εύθραυστα και εξαρτημένα από το περιβάλλον. Σύμφωνα με το Google Testing Blog (2023), τα καλά απομονωμένα unit test εκτελούνται σε χιλιοστά του δευτερολέπτου, ενώ τα integration test σε δευτερόλεπτα και λεπτά.

Πέντε τύποι Test Doubles

Η ταξινόμηση του Gerard Meszaros περιλαμβάνει πέντε τύπους Test Doubles, που διαφέρουν ως προς τη συμπεριφορά και τον σκοπό χρήσης. Η κατανόηση της διαφοράς μεταξύ τους αποτελεί τη βάση του σωστού unit testing.

Dummy

Dummy — αντικείμενο που περνάται στη μέθοδο υπό έλεγχο αλλά δεν χρησιμοποιείται ποτέ. Το Dummy χρειάζεται μόνο για να ικανοποιήσει την υπογραφή της μεθόδου. Στην Kotlin είναι συχνά null, emptyList() ή ένα αντικείμενο με stub. Το Dummy δεν πρέπει να περιέχει καμία λογική — αν κληθεί, το τεστ πρέπει να αποτύχει.

Fake

Fake — απλοποιημένη αλλά λειτουργική υλοποίηση ενός interface. Σε αντίθεση με τα Mock και Stub, το Fake περιέχει πραγματική επιχειρησιακή λογική, αλλά σε απλοποιημένη μορφή. Κλασικό παράδειγμα — το InMemoryUserRepository, το οποίο αποθηκεύει δεδομένα σε HashMap αντί για βάση δεδομένων. Το Fake χρησιμοποιείται όταν πρέπει να ελεγχθεί λογική που εξαρτάται από την κατάσταση, αλλά χωρίς το κόστος της πραγματικής υποδομής.

ΤύποςΣκοπόςΠαράδειγμα
DummyΣυμπλήρωση παραμέτρουnull, κενό αντικείμενο
FakeΛειτουργική απλοποιημένη υλοποίησηInMemoryRepository
StubΕπιστροφή σταθερής τιμήςwhen(api.getUser()).thenReturn(user)
SpyΚαταγραφή κλήσεων για έλεγχοverify(spy).save(user)
MockΈλεγχος αλληλεπίδρασηςverify(mock).sendEmail(email)

Stub

Stub επιστρέφει προκαθορισμένες τιμές σε συγκεκριμένες κλήσεις. Το Stub δεν ελέγχει αν κλήθηκε — απλώς παρέχει δεδομένα. Στο Mockito το Stub δημιουργείται με το when(method).thenReturn(value). Το Stub είναι ιδανικό για έλεγχο όταν χρειάζεται η εξάρτηση να επιστρέψει μια συγκεκριμένη τιμή, αλλά το ίδιο το γεγονός της κλήσης δεν είναι σημαντικό.

Spy

Spy — περιτύλιγμα γύρω από ένα πραγματικό αντικείμενο που καταγράφει όλες τις κλήσεις για μεταγενέστερη επαλήθευση. Σε αντίθεση με το Mock, το Spy προωθεί τις κλήσεις στο πραγματικό αντικείμενο, αλλά επιτρέπει να ελεγχθεί ότι πραγματοποιήθηκαν. Στο Mockito το Spy δημιουργείται με το spy(realObject). Το Spy είναι χρήσιμο για μερικό mocking, όταν θέλουμε να χρησιμοποιήσουμε το πραγματικό αντικείμενο αλλά να ελέγξουμε κάποιες κλήσεις.

Mock

Mock — αντικείμενο με προκαθορισμένες προσδοκίες κλήσεων. Το Mock ελέγχει ότι συγκεκριμένες μέθοδοι κλήθηκαν με συγκεκριμένα ορίσματα και με συγκεκριμένη σειρά. Σε αντίθεση με το Stub, το Mock επικεντρώνεται στην επαλήθευση συμπεριφοράς και όχι στην επιστροφή δεδομένων. Το Mock είναι ο πιο ισχυρός και ο συχνότερα χρησιμοποιούμενος τύπος Test Double στην ανάπτυξη εφαρμογών για κινητά.

Mock και Stub: βασικές διαφορές

Η διαφορά μεταξύ Mock και Stub συχνά προκαλεί σύγχυση ακόμη και σε έμπειρους προγραμματιστές. Η βασική διαφορά είναι στον σκοπό: το Stub ελέγχει την κατάσταση (state verification), το Mock ελέγχει τη συμπεριφορά (behavior verification).

Το Stub απαντά στην ερώτηση: "επέστρεψε ο κώδικας το σωστό αποτέλεσμα;". Το Mock απαντά στην ερώτηση: "κάλεσε ο κώδικας τις σωστές μεθόδους με τα σωστά ορίσματα;". Στην ανάπτυξη εφαρμογών για κινητά, το Stub χρησιμοποιείται όταν έχει σημασία το αποτέλεσμα (π.χ. δεδομένα από το repository), ενώ το Mock όταν έχουν σημασία οι παρενέργειες (π.χ. αποστολή email, εγγραφή στη βάση δεδομένων).

kotlin
// Stub: έλεγχος κατάστασης
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)

// Mock: έλεγχος συμπεριφοράς
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }

Παραδείγματα Test Doubles σε Kotlin

Πρακτικά παραδείγματα και των πέντε τύπων Test Doubles σε Kotlin με τη χρήση του MockK — της πιο δημοφιλούς βιβλιοθήκης mocking για Android έργα.

Fake: InMemoryUserRepository

kotlin
class InMemoryUserRepository : UserRepository {
    private val store = mutableMapOf<String, User>()

    override fun save(user: User) {
        store[user.email] = user
    }

    override fun findByEmail(email: String): User? {
        return store[email]
    }
}

Stub + Mock: τεστ UseCase

kotlin
class RegisterUseCaseTest {
    private val api = mockk<AuthApi>()
    private val repo = spyk(InMemoryUserRepository())
    private val useCase = RegisterUseCase(api, repo)

    fun `register user successfully`() = runTest {
        // Stub: επιστρέφουμε σταθερή API απάντηση
        coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")

        val result = useCase.execute("test@test.com")

        // Verify: ελέγχουμε ότι ο χρήστης αποθηκεύτηκε
        verify { repo.save(any()) }
        assertTrue(result.isSuccess())
    }
}

Dummy: τεστ με αχρησιμοποίητη παράμετρο

kotlin
data class Logger(val appContext: Context, val format: FormatType)

fun `test logger with dummy context`() {
    // Dummy: το Context δεν χρησιμοποιείται μέσα στο Logger
    val dummyContext = mockk<Context>()
    val logger = Logger(dummyContext, FormatType.JSON)
    assertEquals(FormatType.JSON, logger.format)
}

Πότε να χρησιμοποιείται κάθε τύπος στην ανάπτυξη εφαρμογών για κινητά

Η επιλογή του τύπου Test Double εξαρτάται από το τι ακριβώς ελέγχεται: κατάσταση, συμπεριφορά ή ενοποίηση. Στην ανάπτυξη εφαρμογών για κινητά σε Android και iOS διαμορφώθηκαν οι εξής συστάσεις.

Για ViewModel και UseCase

Κατά τον έλεγχο του ViewModel, χρησιμοποιήστε Mock για εξαρτήσεις που παράγουν παρενέργειες (repositories, analytics, πλοήγηση) και Stub για εξαρτήσεις που επιστρέφουν δεδομένα (API clients, ContentProvider). Αυτό επιτρέπει να ελεγχθεί ότι το ViewModel διαχειρίζεται σωστά τόσο τα επιτυχημένα όσο και τα εσφαλμένα σενάρια.

Για Repository και Data Layer

Στο επίπεδο του Repository προτιμώνται τα Fake (in-memory υλοποιήσεις βάσης δεδομένων) και τα Stub (σταθερές API απαντήσεις). Το Fake επιτρέπει τον έλεγχο της λογικής προσωρινής αποθήκευσης και της offline λειτουργίας χωρίς ρύθμιση του SQLite. Το Stub προσομοιώνει διάφορες HTTP καταστάσεις: 200, 404, 500, timeout.

  • Unit test επιχειρησιακής λογικής — Mock για όλες τις εξωτερικές εξαρτήσεις, Dummy για αχρησιμοποίητες παραμέτρους
  • Integration test — Fake αντί για Mock (ελέγχουμε ότι τα στοιχεία λειτουργούν μαζί)
  • UI test — Stub για API απαντήσεις (μέσω MockWebServer ή WireMock)
  • Τεστ προσωρινής αποθήκευσης — Fake για τη βάση δεδομένων (in-memory αντί για Room/SQLite)
  • Ασύγχρονα τεστ — Mock με υποστήριξη coroutine (MockK + Turbine για Flow)

Τυπικά λάθη κατά τη χρήση υποκατάστατων

Η λανθασμένη χρήση των Test Doubles — μία από τις συχνότερες αιτίες εύθραυστων τεστ που σπάνε σε κάθε refactoring.

Over-mocking: υπερβολική χρήση Mock

Το πιο συνηθισμένο λάθος — να γίνεται mocking για όλα. Αν κάθε εξάρτηση στο τεστ έχει αντικατασταθεί με Mock, το τεστ σταματά να ελέγχει την πραγματική συμπεριφορά. Το Mock πρέπει να χρησιμοποιείται μόνο για εξωτερικές εξαρτήσεις (δίκτυο, βάση δεδομένων, σύστημα αρχείων, υπηρεσίες συστήματος). Τα εσωτερικά στοιχεία της εφαρμογής (Value Object, data class, απλά utilities) δεν πρέπει να αντικαθίστανται.

Under-specification: ανεπαρκής προδιαγραφή

Το δεύτερο λάθος — δημιουργία Mock χωρίς καθορισμένες προσδοκίες. Αν μια μέθοδος κληθεί χωρίς every / when, το Mock επιστρέφει την προεπιλεγμένη τιμή (null, 0, false). Αυτό μπορεί να οδηγήσει σε ψευδώς θετικά τεστ, όταν το Mock επιστρέφει σιωπηλά null και το τεστ το ερμηνεύει ως σωστή συμπεριφορά.

Over-verification: υπερβολική επαλήθευση

Το τρίτο λάθος — ο έλεγχος κάθε κλήσης κάθε Mock. Το Verify πρέπει να χρησιμοποιείται μόνο για κλήσεις που είναι κρίσιμες από άποψη επιχειρησιακής λογικής. Η υπερβολική επαλήθευση καθιστά τα τεστ εύθραυστα: η αλλαγή της σειράς των κλήσεων στον production κώδικα σπάει τα τεστ χωρίς αλλαγή της συμπεριφοράς.

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

Ποια είναι η διαφορά μεταξύ Mock και Stub;

Stub επιστρέφει δεδομένα και ελέγχει την κατάσταση (τι επιστράφηκε), ενώ το Mock ελέγχει τη συμπεριφορά (ποιες μέθοδοι κλήθηκαν). Stub = "επίστρεψε X", Mock = "έλεγξε ότι κάλεσαν το Y με όρισμα Z". Στα πραγματικά τεστ, ένα αντικείμενο συχνά λειτουργεί ταυτόχρονα και ως Stub και ως Mock.

Πότε να χρησιμοποιώ Fake αντί για Mock;

Fake προτιμάται από το Mock όταν ελέγχεται λογική που εξαρτάται από την κατάσταση: προσωρινή αποθήκευση, offline λειτουργία, συναλλαγές. Το Fake (in-memory υλοποίηση) επιτρέπει τον έλεγχο αυτών των σεναρίων χωρίς εύθραυστες κλήσεις verify. Το Mock είναι καλύτερο για τον έλεγχο αποστολής δεδομένων: analytics, push, email.

Ποια βιβλιοθήκη Test Doubles είναι καλύτερη για Android;

Για Android έργα σε Kotlin συνιστάται το MockK. Υποστηρίζει coroutines, suspend συναρτήσεις, sealed class και extension συναρτήσεις χωρίς πρόσθετες ρυθμίσεις. Για έργα σε Java, το πρότυπο παραμένει το Mockito — η πιο δημοφιλής βιβλιοθήκη με εκτενή τεκμηρίωση.

Πώς να ελέγξω το Kotlin Flow με Test Doubles;

Για τον έλεγχο του Kotlin Flow χρησιμοποιήστε τη βιβλιοθήκη Turbine σε συνδυασμό με το MockK. Το Turbine απλοποιεί τον έλεγχο της εκπομπής Flow: μπορείτε να ελέγξετε τη σειρά των τιμών, την ολοκλήρωση της ροής και τις εξαιρέσεις. Το Stub για Flow επιστρέφει flowOf(value), το Mock ελέγχει ότι το Flow συλλέχθηκε.

Επιτρέπεται η χρήση Test Doubles σε UI test;

Ναι, αλλά στο επίπεδο των API απαντήσεων, όχι των στοιχείων UI. Οι βιβλιοθήκες MockWebServer (OkHttp) και WireMock επιτρέπουν την υποκατάσταση HTTP απαντήσεων σε UI test. Τα ίδια τα στοιχεία UI (Compose, SwiftUI Views) δεν πρέπει να αντικαθίστανται — η συμπεριφορά τους ελέγχεται μέσω screenshot test και Espresso.

Σύνοψη

  • Test Doubles — γενικός όρος για πέντε τύπους αντικειμένων υποκατάστασης: Mock, Stub, Fake, Spy, Dummy
  • Mock ελέγχει τη συμπεριφορά (verify), Stub επιστρέφει δεδομένα (thenReturn), Fake — λειτουργεί ως απλοποιημένη πραγματική υλοποίηση
  • Spy περιτυλίσσει το πραγματικό αντικείμενο και καταγράφει τις κλήσεις, Dummy συμπληρώνει αχρησιμοποίητες παραμέτρους
  • Η τυπολογία του Gerard Meszaros — κανονική ταξινόμηση που χρησιμοποιείται σε όλα τα σύγχρονα mocking frameworks
  • Για Kotlin έργα συνιστάται το MockK, για Java — Mockito, για iOS — Cuckoo ή OHHTTPStubs
  • Τυπικά λάθη: over-mocking (αντικατάσταση των πάντων), under-specification (απροσδιόριστες προσδοκίες), over-verification (υπερβολικά verify)
  • Fake προτιμάται από το Mock κατά τον έλεγχο λογικής με κατάσταση — προσωρινής αποθήκευσης, offline λειτουργίας και συναλλαγών

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

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

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

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