Unittesten in mobiele ontwikkeling: wat is het, methoden en frameworks

Auteur: IT Sectr Gepubliceerd: 2026-04-06 Leestijd: 9 min

Unittesten is een methode voor softwareverificatie waarbij de correctheid van individuele modules of codefuncties in isolatie van de rest van het systeem wordt getest. Volgens Martin Fowler, 2026 vormen unittesten de basis van CI/CD en refactoring en bieden ze snelle feedback over de werking van code. Modulair testen helpt fouten in een vroeg stadium van ontwikkeling te ontdekken, waardoor de kosten voor het oplossen ervan tientallen keren worden verlaagd.

Belangrijkste punten

  • Unittest — controle van één module (functie, methode, klasse) in isolatie van externe afhankelijkheden
  • FIRST-principes — Fast, Isolated, Repeatable, Self-validating, Timely — basis van kwalitatieve tests
  • Mocks en stubs — vervangers van externe afhankelijkheden (database, API, bestandssysteem) die testisolatie garanderen
  • TDD (Test-Driven Development) — ontwikkelmethodologie door testen: rood-groen-refactoring
  • Testpiramide — unittesten vormen 70% van de piramide en bieden snelle feedback bij elke commit

Wat is unittesten?

Unittesten is het proces van het controleren van individuele eenheden (unit) van broncode — functies, methoden, klassen — in isolatie van de rest van het programma. Elke test voert een specifiek gebruiks scenario van de module uit en controleert of het resultaat overeenkomt met de verwachting. Unittesten worden geschreven in dezelfde programmeertaal als de hoofdcode en worden automatisch uitgevoerd in de ontwikkelomgeving of in de CI/CD-pijplijn. In tegenstelling tot integratietests interacteren unittesten niet met echte databases, bestandssystemen of netwerkdiensten.

Waarom zijn unittesten nodig?

Het hoofddoel is snelle feedback over de correctheid van code na wijzigingen. Als een ontwikkelaar een methode refactort, bevestigt de set unittesten dat het gedrag niet is verbroken. Volgens Google Testing Blog (2025) hebben projecten met een unittestdekking van meer dan 60% 2,5 keer minder productie incidenten. Extra voordelen: codedocumentatie (tests tonen hoe de API te gebruiken), vereenvoudiging van refactoring (implementatie kan worden gewijzigd met behoud van gedrag) en snelle diagnose van regressies.

Wat wordt als unittest beschouwd?

Niet elke automatische test is een unittest. Criteria: één module (klasse of functie) wordt getest, externe afhankelijkheden worden vervangen door mocks of stubs, de test wordt in milliseconden uitgevoerd, vereist geen server of database. Een test die een echte database benadert, is een integratietest. Een test die een browser opent, is een E2E-test. Inzicht in de grenzen tussen testtypen is belangrijk voor de juiste verdeling van inspanningen in de testpiramide.

FIRST-principes en AAA-structuur

Kwalitatieve unittesten volgen de FIRST-principes geformuleerd door Robert C. Martin. Elke test moet Fast (snel — milliseconden), Isolated (geïsoleerd — niet afhankelijk van andere tests), Repeatable (herhaalbaar — hetzelfde resultaat op elke machine), Self-validating (zelfvaliderend — resultaat is „passed” of „failed”, zonder handmatige controle) en Timely (tijdig — geschreven vóór of gelijktijdig met code) zijn. Schending van een van de principes vermindert de testwaarde.

AAA-structuur (Arrange-Act-Assert)

Standaard sjabloon voor het schrijven van unittesten. Arrange — voorbereiding van gegevens en afhankelijkheden: aanmaken van objecten, configureren van mocks, instellen van invoerparameters. Act — uitvoeren van de geteste actie: aanroepen van methode of functie. Assert — controleren van het resultaat: vergelijken van werkelijke waarde met verwachte waarde. Verdeling in drie blokken maakt de test leesbaar en begrijpelijk. Als het Assert-blok complexe logica vereist, test de test waarschijnlijk te veel tegelijk.

kotlin
// Voorbeeld van unittest volgens AAA-sjabloon in Kotlin met JUnit 5
class CalculatorTest {

    private lateinit var calculator: Calculator

    @BeforeEach
    fun setUp() {
        // ARRANGE — we maken het te testen object
        calculator = Calculator()
    }

    @Test
    fun addition_shouldReturnCorrectSum() {
        // ACT — we voeren de actie uit
        val result = calculator.add(2, 3)

        // ASSERT — we controleren het resultaat
        Assertions.assertEquals(5, result)
    }
}

Naamgeving van tests

De testnaam moet beschrijven wat wordt gecontroleerd en welk resultaat wordt verwacht. Formaat: [methodName]_[scenario]_[expectedResult]. Voorbeeld: calculateTotal_whenDiscountApplied_shouldReturnDiscountedTotal. Een goede testnaam vervangt een opmerking en geeft bij falen direct aan welke functionaliteit is verbroken. Vermijd namen als test1, checkSomething of verify — ze dragen geen informatie en bemoeilijken diagnose.

Mocks, stubs en fakes: wat en wanneer gebruiken

Voor isolatie van de geteste module van externe afhankelijkheden worden testdoubles gebruikt. Hoofdtypen: mocks — controleren of een bepaalde methode met de juiste parameters is aangeroepen; stubs — retourneren vooraf gedefinieerde waarden bij aanroep; fakes — vereenvoudigde implementatie van een echte component (bijv. InMemoryUserRepository in plaats van UserRepository die met database werkt). Keuze hangt af van wat moet worden gecontroleerd: toestand (stub) of interactie (mock).

DoubleWat controleertVoorbeeld
MockAanroep methode met juiste parametersuserRepository.save(user) is exact 1 keer aangeroepen
StubRetourwaarderepository.findById(1) retourneert User(id=1, name=„Test”)
FakeLogica via vereenvoudigde implementatieInMemoryMapUserRepository met HashMap i.p.v. database
SpyGedeeltelijk mocken van echt objectspy(repo).when(findById).thenReturn(user)

Mockito: voorbeeld van mocken in Java/Kotlin

Mockito is het populairste framework voor mocken in Java en Kotlin. Het maakt het mogelijk mocks te maken via mock(), retourwaarden in te stellen via when().thenReturn() en aanroepen te controleren via verify(). Moderne versies van Mockito (5.x) ondersteunen statische mocks (mockStatic) en vereenvoudigde syntaxis via BDDMockito (given-willReturn). Belangrijke regel: mock niet wat niet van jou is — maak geen mocks voor waardeobjecten en standaardbibliotheken.

kotlin
// Voorbeeld van unittest met Mockito in Kotlin
class OrderServiceTest {

    @Mock
    private lateinit var paymentGateway: PaymentGateway

    @Mock
    private lateinit var userRepository: UserRepository

    private lateinit var orderService: OrderService

    @BeforeEach
    fun init() {
        MockitoAnnotations.openMocks(this)
        orderService = OrderService(paymentGateway, userRepository)
    }

    @Test
    fun processOrder_whenPaymentFails_shouldThrowException() {
        // given
        val user = User(id = 1, balance = 100.0)
        val order = Order(amount = 200.0)
        Mockito.`when`(paymentGateway.charge(any())).thenReturn(false)
        Mockito.`when`(userRepository.findById(1)).thenReturn(user)

        // when & then
        assert Throws<PaymentException> {
            orderService.processOrder(user.id, order)
        }

        // verify
        Mockito.verify(paymentGateway).charge(any())
    }
}

TDD: ontwikkeling door testen

TDD (Test-Driven Development) — methodologie waarbij de test vóór de implementatie van code wordt geschreven. Cyclus „Red-Green-Refactor”: schrijf een test die faalt (Red), schrijf minimale code om de test te laten slagen (Green), verbeter code zonder gedrag te wijzigen (Refactor). TDD garandeert dat alle code door tests wordt gedekt (dekking = 100% voor geschreven functionaliteit) en dat code testbaar is — als code moeilijk te testen is, betekent dit dat de architectuur verbetering behoeft.

Voordelen van TDD

Volgens onderzoek van IBM (2006-2026, longitudinale studie) maken teams die TDD gebruiken 40-80% minder defecten in productie in vergelijking met teams die tests na code schrijven. TDD verbetert ook de architectuur: de ontwikkelaar moet vóór implementatie over het API-ontwerp nadenken, wat leidt tot losse koppeling (loose coupling) en hoge cohesie (high cohesion). Een extra effect — documentatie met levende code: tests dienen als specificatie van modulegedrag die altijd actueel is.

Wanneer is TDD niet geschikt?

TDD is niet altijd optimaal. UI-componenten zijn moeilijk in isolatie te testen — hiervoor zijn snapshot-tests of visuele regressietests (Percy, Chromatic) effectiever. Prototyping en onderzoek (spike solutions) vereisen geen tests. Legacy-code zonder tests is moeilijk via TDD te dekken — hier zijn eerst characterization tests nodig (tests die huidig gedrag vastleggen vóór refactoring). In deze gevallen wordt TDD niet volledig afgeschaft maar aangepast — tests worden geschreven voor gewijzigde functionaliteit, niet voor alle legacy-code.

Unittesten in mobiele applicaties

Mobiele ontwikkeling heeft zijn eigenheid: bedrijfslogica is vaak vermengd met UI-code (Activity, ViewController, ViewModel), wat unittesten bemoeilijkt. Beste praktijk — dunne views, dikke ViewModels: verplaats alle logica van UI-componenten naar aparte klassen (UseCase, Repository, ViewModel) die gemakkelijk zonder emulator te testen zijn. Voor Android en iOS bestaan native unittest-frameworks die op JVM/Native werken zonder het apparaat te starten.

Unittesten op Android (JUnit + Mockito/Robolectric)

Android-unittesten worden uitgevoerd op lokale JVM zonder emulator, wat uitvoeringsnelheid garandeert — een typische test duurt minder dan 100ms. JUnit 5 is de hoofdrunner. Voor ViewModel-tests gebruikt u kotlinx-coroutines-test voor het testen van coroutines en Turbine voor het testen van StateFlow. Robolectric maakt het testen van Android-afhankelijke componenten (Context, Resources) zonder emulator mogelijk door shadow-klassen te laden. Voor Compose-tests gebruikt u Compose UI Test — maar dit zijn al UI-tests, geen unittesten.

Unittesten op iOS (XCTest + Quick/Nimble)

iOS-unittesten worden geschreven in Swift met XCTest (ingebouwd in Xcode). Quick + Nimble — BDD-frameworks voor leesbaardere tests (describe/context/it). Voor mocken gebruikt u Cuckoo (generatie van mocks) of SwiftyMocky. Swift ondersteunt protocollen en dependency-injectie, wat het vervangen van afhankelijkheden vergemakkelijkt. Belangrijk punt: iOS-unittesten worden uitgevoerd op macOS-simulator, niet op een echt apparaat. Tests die hardwarefuncties (camera, Bluetooth) vereisen, zijn integratietests.

Unittesten op Flutter (flutter_test + Mockito)

Flutter-unittesten gebruiken het flutter_test-pakket en worden uitgevoerd op Dart VM zonder emulator. Voor mocken — het mockito-pakket met codegenerator (build_runner). Widget-tests (in hetzelfde pakket) testen individuele widgets, maar vereisen rendering en werken langzamer — gebruik ze alleen voor het controleren van UI-logica. Pure Dart-logica (modellen, repositories, blobs) wordt getest als gewone Dart-tests zonder flutter_test te importeren.

dart
// Voorbeeld van unittest op Flutter met mockito
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';

@GenerateMocks([ApiClient])
import 'user_repository_test.mocks.dart';

void main() {
    late MockApiClient mockApi;
    late UserRepository repository;

    setUp(() {
        mockApi = MockApiClient();
        repository = UserRepository(mockApi);
    });

    test('fetchUser returns user when API succeeds', () async {
        // Arrange
        final expectedUser = User(id: 1, name: 'Test');
        when(mockApi.getUser(1))
            .thenAnswer((_) async => expectedUser);

        // Act
        final result = await repository.fetchUser(1);

        // Assert
        expect(result, expectedUser);
        verify(mockApi.getUser(1)).called(1);
    });
}

Beste praktijken en typische fouten

Effectief unittesten vereist discipline. Hoofdregel: test gedrag, niet implementatie. De test mag niet weten hoe de module intern is geïmplementeerd (welke privémethoden worden aangeroepen, in welke volgorde). Als de test aan implementatie is gekoppeld, breekt hij bij elke refactoring en verliest hij waarde. De test controleert een contract: bij invoer X moet uitvoer Y zijn. Uitzondering — tests voor algoritmen met kritieke prestaties, waar de volgorde van aanroepen belangrijk is.

  • Één controle per test — één assert of één groep gerelateerde asserts voor één logische controle
  • Vermijd duplicatie — gebruik @BeforeEach / setUp voor gedeelde initialisatie, geparametriseerde tests voor verschillende invoergegevens
  • Test geen privémethoden — test via publieke API. Als een privémethode niet wordt gedekt, is de logica ervan niet van buitenaf zichtbaar
  • Dek randgevallen — lege collecties, null/undefined, negatieve getallen, maximale waarden
  • Gebruik geen Thread.sleep in tests — dit maakt tests traag en onstabiel. Gebruik test-timeouts en coroutines

Welk dekkingsniveau is voldoende?

100% dekking is een onbereikbaar en onnodig doel. Volgens Google Testing Blog (2025) is het optimale dekkingsniveau voor unittesten 70-80% van de coderegels. 100% dekking wordt vaak bereikt door getters, setters en constructors te testen, wat geen waarde toevoegt. Focus op kritieke bedrijfslogica: complexe berekeningen, validatie, foutafhandeling, randgevallen. Gebruik JaCoCo (Java), Coverage.py (Python), Istanbul (JS) voor meting en stel een drempel in CI in — build-falen bij dekking onder 60%.

CI/CD en unittesten

Unittesten zijn de eerste fase van elke CI/CD-pijplijn. Ze worden uitgevoerd bij elke push naar de repository, vóór build en deploy. De gemiddelde uitvoeringstijd van unittesten in een project mag niet meer dan 5 minuten bedragen — als het langer duurt, zijn tests niet meer „snel” en ontwikkelaars stoppen met lokaal uitvoeren. Deel tests in snelle (unit) en langzame (integratie) en voer ze uit in verschillende fasen van de pijplijn. Gebruik parallelle uitvoering en fail-fast voor versnelling.

Veelgestelde vragen

Waarin verschilt een unittest van een integratietest?

Een unittest controleert één module in isolatie, vervangt externe afhankelijkheden door mocks. Een integratietest controleert interactie tussen meerdere echte componenten (database, API, bestandssysteem). Unittesten worden uitgevoerd in milliseconden, integratietests in seconden. In de testpiramide vormen unittesten 70%.

Welk framework kiezen voor unittesten?

Keuze hangt af van platform: JUnit 5 voor Java/Kotlin, XCTest voor iOS/Swift, pytest voor Python, Jest/Vitest voor JavaScript/TypeScript, flutter_test voor Flutter. Voor mocken gebruikt u Mockito (Java), Cuckoo (iOS), unittest.mock (Python) of vitest.mock (JS). Alle moderne frameworks ondersteunen geparametriseerde tests, ingebouwde asserts en parallelle uitvoering.

Wat zijn de F.I.R.S.T.-principes in testen?

Fast — test wordt in milliseconden uitgevoerd. Isolated — niet afhankelijk van andere tests en externe systemen. Repeatable — geeft hetzelfde resultaat op elke machine. Self-validating — controleert resultaat automatisch. Timely — geschreven vóór of synchroon met code. Schending van een van de principes vermindert de testefficiëntie.

Moet ik unittesten schrijven voor ViewModel in Android/iOS?

Ja, verplicht. ViewModel bevat bedrijfslogica — gebeurtenisafhandeling, gegevenstransformatie, statusbeheer. Op Android gebruikt u kotlinx-coroutines-test voor coroutines en Turbine voor StateFlow-testen. Op iOS test u Combine Publishers of async/await in ViewModel. ViewModel-tests zijn pure unittesten die op JVM/macOS zonder emulator werken.

Hoe test ik code met netwerkverzoeken?

Netwerkverzoeken worden in unittesten niet uitgevoerd — ze worden vervangen door mocks van de HTTP-client. Op Android gebruikt u MockWebServer (OkHttp) — dit start een lokale HTTP-server, wat de voorkeur heeft boven mocks omdat het echte netwerkinteractie reproduceert. MockWebServer biedt isolatie zonder realisme te verliezen. Voor iOS — OHHTTPStubs of URLProtocol voor het onderscheppen en vervangen van antwoorden.

Samenvatting

  • Unittesten — controleren van individuele modules in isolatie van externe afhankelijkheden met snelle feedback
  • AAA-structuur — Arrange (voorbereiding), Act (actie), Assert (controle) — standaard testsjabloon
  • Mocks en stubs — testdoubles voor isolatie: mocks controleren aanroepen, stubs retourneren waarden
  • TDD — ontwikkeling door testen (Red-Green-Refactor) vermindert defecten met 40-80%
  • FIRST-principes — Fast, Isolated, Repeatable, Self-validating, Timely — basis van kwalitatieve test
  • Platformtools — JUnit 5 (Android), XCTest (iOS), flutter_test (Flutter), Jest (React Native)
  • Dekking 70-80% — optimaal niveau voor kritieke bedrijfslogica, getters en setters vereisen geen tests

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook