Testen beheersen: JUnit, Mockito en integratietesten · Les

Statische methoden en constructors mocken

Verken geavanceerde technieken om statische methoden, final-klassen en constructors te mocken met Mockito-extensies zoals `mockito-inline`.

Les 2 van 411 stappen

Statische methoden en constructors mocken is een gratis Testen beheersen: JUnit, Mockito en integratietesten-les op CoddyKit. Dit is les 2 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Testen beheersen: JUnit, Mockito en integratietesten. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Testen beheersen: JUnit, Mockito en integratietesten bevat in totaal 4 lessen.

Verder dan gewone mocks

Welkom! Tot nu toe heb je geleerd hoe je interfaces en gewone klassen mockt met Mockito. Maar hoe zit het met die lastige stukjes code die moeilijk te testen lijken?

Soms kom je statische methoden, finale klassen of directe constructoraanroepen (new MyObject()) tegen in de code die je wilt testen. Met standaard Mockito kun je hier niet rechtstreeks mee werken.

Wanneer traditionele mocks tekortschieten

Waarom moeten we deze mocken?

  • Statische hulpmethoden: worden vaak gebruikt voor algemene taken, maar zijn moeilijk te isoleren als ze neveneffecten of externe afhankelijkheden hebben.
  • Finale klassen en methoden: kunnen niet worden overgeërfd of overschreven, waardoor traditioneel mocken op basis van proxies onmogelijk is.
  • Constructoraanroepen (new): directe objectcreatie binnen een methode maakt het moeilijk om in plaats daarvan een mock te injecteren.

Door deze te mocken kun je de code die je test isoleren, zelfs in complexe of verouderde systemen.

Mockito uitbreiden

Om deze beperkingen te overwinnen biedt Mockito geavanceerde mogelijkheden via zijn inline mock maker, die vaak wordt aangeduid als mockito-inline.

Deze functie gebruikt manipulatie van bytecode om klassen tijdens runtime te "herschrijven", waardoor Mockito dingen kan mocken die normaal gesproken niet kunnen worden gemockt, zoals statische methoden, finale klassen en zelfs constructors.

Het is een krachtig hulpmiddel, maar gebruik het doordacht!

Je project instellen

Voor Mockito-versies 3.4.0 en nieuwer is de inline mock maker vaak standaard ingeschakeld of beschikbaar door simpelweg de standaardafhankelijkheid mockito-core te gebruiken.

Als je een oudere versie gebruikt of problemen ondervindt, moet je mogelijk expliciet mockito-inline declareren. Zorg er bij de meeste moderne configuraties gewoon voor dat je een recente versie van mockito-core hebt.

Zo kan je build.gradle eruitzien:

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
    testImplementation 'org.mockito:mockito-core:5.8.0' // Includes inline mock maker
    // For older Mockito versions, you might need:
    // testImplementation 'org.mockito:mockito-inline:5.8.0'
}

Statische hulpmethoden mocken

Om statische methoden te mocken biedt Mockito de interface MockedStatic. Je maakt een mockcontext met Mockito.mockStatic() binnen een blok try-with-resources. Zo blijft de mock alleen actief zolang dat blok wordt uitgevoerd.

Bekijk een voorbeeld met een hul klasse:

import org.mockito.Mockito;
import org.mockito.MockedStatic;

// Utility class with a static method
class MyStaticService {
    public static String getGreeting() {
        return "Hello from real static service!";
    }
    public static int add(int a, int b) {
        return a + b;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Before mock: " + MyStaticService.getGreeting());

        // Mock the static method within a try-with-resources
        try (MockedStatic<MyStaticService> mockedStatic = Mockito.mockStatic(MyStaticService.class)) {
            mockedStatic.when(MyStaticService::getGreeting).thenReturn("Hello from mocked static!");
            mockedStatic.when(() -> MyStaticService.add(1, 2)).thenReturn(100);

            System.out.println("During mock (greeting): " + MyStaticService.getGreeting());
            System.out.println("During mock (add): " + MyStaticService.add(1, 2));
            System.out.println("During mock (add other): " + MyStaticService.add(5, 5)); // Not mocked, returns 0 by default

            // Verify calls (typically in a test assertion)
            mockedStatic.verify(MyStaticService::getGreeting);
            mockedStatic.verify(() -> MyStaticService.add(1, 2), Mockito.times(1));

        } // Mock is closed here

        System.out.println("After mock: " + MyStaticService.getGreeting());
    }
}

Statische interacties controleren

Net als bij gewone mocks kun je interacties met statische methoden verifiëren. Gebruik de methode verify() op je object van het type MockedStatic.

Zo controleer je of je code onder test de statische methode zoals verwacht heeft aangeroepen, met de juiste argumenten en het juiste aantal aanroepen.

Let op Mockito.times(1) in het vorige voorbeeld om het aantal aanroepen te controleren.

import org.mockito.Mockito;
import org.mockito.MockedStatic;

// Class from previous scene
class MyStaticService {
    public static String getGreeting() { return "Hello from real static!"; }
    public static int multiply(int a, int b) { return a * b; }
}

public class Main {
    public static void main(String[] args) {
        // Simulate a scenario where a static method is called
        System.out.println("Initial call: " + MyStaticService.multiply(2, 3));

        try (MockedStatic<MyStaticService> mockedStatic = Mockito.mockStatic(MyStaticService.class)) {
            mockedStatic.when(() -> MyStaticService.multiply(2, 3)).thenReturn(777);
            mockedStatic.when(() -> MyStaticService.multiply(5, 5)).thenReturn(123);

            // Call the static method through the mock
            System.out.println("Mocked call 1: " + MyStaticService.multiply(2, 3));
            System.out.println("Mocked call 2: " + MyStaticService.multiply(5, 5));

            // Verify specific calls
            mockedStatic.verify(() -> MyStaticService.multiply(2, 3), Mockito.times(1));
            mockedStatic.verify(() -> MyStaticService.multiply(5, 5), Mockito.atLeastOnce());
            // Verify that a different call was NOT made
            mockedStatic.verify(() -> MyStaticService.multiply(10, 10), Mockito.never());

            System.out.println("Verifications completed.");

        } // Mock is closed here
    }
}

Finale klassen temmen

De mock maker mockito-inline maakt het ook mogelijk om finale klassen en finale methoden te mocken. Dit is vooral nuttig bij bibliotheken van derden of verouderde code waarvan je het klasseontwerp niet eenvoudig kunt wijzigen.

Je mockt finale klassen net als gewone klassen: Mockito.mock(FinalClass.class).

import org.mockito.Mockito;

// A final class that cannot be extended normally
final class FinalProcessor {
    public final String process(String input) {
        return "Real processed: " + input.toUpperCase();
    }
    public int getVersion() {
        return 1;
    }
}

public class Main {
    public static void main(String[] args) {
        FinalProcessor realProcessor = new FinalProcessor();
        System.out.println("Real output: " + realProcessor.process("data"));
        System.out.println("Real version: " + realProcessor.getVersion());

        // Mocking a final class
        FinalProcessor mockProcessor = Mockito.mock(FinalProcessor.class);

        // Stubbing a final method
        Mockito.when(mockProcessor.process("data")).thenReturn("Mocked processed: data");
        Mockito.when(mockProcessor.getVersion()).thenReturn(99);

        System.out.println("Mocked output: " + mockProcessor.process("data"));
        System.out.println("Mocked version: " + mockProcessor.getVersion());

        // Verify interaction with the mocked final class
        Mockito.verify(mockProcessor).process("data");
    }
}

Nieuwe objecten onderscheppen

Wat als je code rechtstreeks nieuwe objecten maakt met new MyObject()? Met MockedConstruction kun je deze aanroepen onderscheppen en in plaats van echte instanties mockinstanties retourneren.

Dit is essentieel voor het testen van klassen die hun afhankelijkheden intern beheren in plaats van ze via afhankelijkheidsinjectie te ontvangen.

import org.mockito.Mockito;
import org.mockito.MockedConstruction;

// A class that creates another object internally
class DependentService {
    private final Helper helper;

    public DependentService() {
        this.helper = new Helper(); // Direct constructor call
    }

    public String doWork() {
        return "Service working with: " + helper.getGreeting();
    }
}

// The class whose constructor we want to mock
class Helper {
    public String getGreeting() {
        return "Real Helper greeting";
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Without mock:");
        DependentService realService = new DependentService();
        System.out.println(realService.doWork());

        System.out.println("\nWith mocked construction:");
        // Mock the constructor of Helper
        try (MockedConstruction<Helper> mockedConstruction = Mockito.mockConstruction(Helper.class,
                (mock, context) -> {
                    // This block runs every time new Helper() is called
                    Mockito.when(mock.getGreeting()).thenReturn("Mocked Helper greeting!");
                    System.out.println("Helper constructor intercepted!");
                })) {

            // When DependentService() is called, it calls new Helper(),
            // which now returns our stubbed mock.
            DependentService serviceWithMockedHelper = new DependentService();
            System.out.println(serviceWithMockedHelper.doWork());

            // You can get all constructed mocks
            Helper constructedMock = mockedConstruction.constructed().get(0);
            Mockito.verify(constructedMock).getGreeting(); // Verify interaction with the mock
            System.out.println("Verified mock interaction.");

        } // MockedConstruction is closed here
    }
}

Voorzichtig gebruiken

Hoewel het krachtig is, kan het mocken van statische methoden en constructors erop wijzen dat het ontwerp moeilijk te testen is.

  • Ontwerpgeur: wanneer je sterk op deze mocks leunt, kan dat erop wijzen dat je klassen te nauw gekoppeld zijn of de principes van afhankelijkheidsinjectie niet volgen.
  • Leesbaarheid: tests met deze geavanceerde mocks kunnen moeilijker te begrijpen en te onderhouden zijn.
  • Wanneer gebruiken: vooral geschikt voor verouderde code, bibliotheken van derden of situaties waarin herstructurering niet direct haalbaar is. Geef waar mogelijk prioriteit aan herstructurering voor betere testbaarheid!

Controle van geavanceerd mocken

Welke van de volgende uitspraken over mockito-inline en geavanceerde mocktechnieken zijn WAAR?

Samenvatting van geavanceerd mocken

Goed gedaan! Je hebt geavanceerde Mockito-technieken verkend:

  • De mock maker mockito-inline maakt het mogelijk om statische methoden, finale klassen en constructors te mocken.
  • Met MockedStatic kun je aanroepen van statische methoden binnen een bepaalde scope stubben en verifiëren.
  • MockedConstruction helpt je objecten die via het sleutelwoord new zijn gemaakt te onderscheppen en te vervangen.

Deze tools zijn krachtig voor het testen van lastige code, maar houd rekening met de onderliggende ontwerppatronen. Vervolgens leer je best practices voor Mockito!

Gratis beginnen

Leer Testen beheersen: JUnit, Mockito en integratietesten met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
12
Lessen
48

Veelgestelde vragen

Is de les “Statische methoden en constructors mocken” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad Testen beheersen: JUnit, Mockito en integratietesten, waaronder “Statische methoden en constructors mocken”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Testen beheersen: JUnit, Mockito en integratietesten bevat in totaal 4 lessen.

Wat leer ik in “Statische methoden en constructors mocken”?

Verken geavanceerde technieken om statische methoden, final-klassen en constructors te mocken met Mockito-extensies zoals `mockito-inline`. Je oefent met Testen beheersen: JUnit, Mockito en integratietesten door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Testen beheersen: JUnit, Mockito en integratietesten te beginnen?

Ervaring vooraf is niet nodig. Testen beheersen: JUnit, Mockito en integratietesten op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “Statische methoden en constructors mocken”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Testen beheersen: JUnit, Mockito en integratietesten?

Ja. Elke les over Testen beheersen: JUnit, Mockito en integratietesten bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Aangepaste antwoorden en callbacks
  2. Statische methoden en constructors mocken
  3. Best practices voor Mockito
  4. Argumenten vastleggen met ArgumentCaptor
← Terug naar Testen beheersen: JUnit, Mockito en integratietesten