Testing i praksis: JUnit, Mockito og integrasjonstester · leksjon

Mocking av statiske metoder og konstruktører

Utforsk avanserte teknikker for å mocke statiske metoder, final-klasser og konstruktører ved hjelp av Mockito-utvidelser som `mockito-inline`.

Leksjon 2 av 411 trinn

Mocking av statiske metoder og konstruktører er en gratis leksjon i Testing i praksis: JUnit, Mockito og integrasjonstester på CoddyKit. Dette er leksjon 2 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Testing i praksis: JUnit, Mockito og integrasjonstester, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Testing i praksis: JUnit, Mockito og integrasjonstester inneholder totalt 4 leksjoner.

Utover vanlige mocker

Velkommen! Så langt har du lært å mocke grensesnitt og vanlige klasser med Mockito. Men hva med de vanskelige delene av koden som virker motstandsdyktige mot testing?

Noen ganger møter du statiske metoder, final-klasser eller direkte konstruktørkall (new MyObject()) i koden du vil teste. Standard Mockito kan ikke håndtere disse direkte.

Når tradisjonelle mocker ikke fungerer

Hvorfor trenger vi å mocke disse?

  • Statiske hjelpemetoder: Brukes ofte til vanlige oppgaver, men er vanskelige å isolere hvis de har sideeffekter eller eksterne avhengigheter.
  • Final-klasser/-metoder: Kan ikke arves fra eller overstyres, noe som gjør tradisjonell proxy-basert mocking umulig.
  • Konstruktørkall (new): Direkte oppretting av objekter i en metode gjør det vanskelig å injisere en mock i stedet.

Mocking av disse gjør det mulig å isolere koden som testes, selv i komplekse eller eldre systemer.

Gi Mockito mer kraft

For å overvinne disse begrensningene tilbyr Mockito avanserte funksjoner gjennom sin inline mock maker, ofte kalt mockito-inline.

Denne funksjonen bruker bytekode-manipulering til å «skrive om» klasser under kjøring, slik at Mockito kan mocke ting den vanligvis ikke kan, for eksempel statiske metoder, final-klasser og til og med konstruktører.

Dette er et kraftig verktøy, men bruk det med omtanke!

Sette opp prosjektet

For Mockito-versjon 3.4.0 og nyere er inline mock maker ofte aktivert som standard eller tilgjengelig bare ved å bruke den vanlige mockito-core-avhengigheten.

Hvis du bruker en eldre versjon eller får problemer, kan det hende du må deklarere mockito-inline eksplisitt. For de fleste moderne oppsett trenger du bare å sørge for at du har en nyere versjon av mockito-core.

Slik kan build.gradle se ut:

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'
}

Mocking av statiske hjelpere

For å mocke statiske metoder tilbyr Mockito grensesnittet MockedStatic. Du oppretter en mock-kontekst ved hjelp av Mockito.mockStatic() i en try-with-resources-blokk. Dette sikrer at mocken bare er aktiv så lenge blokken kjører.

La oss se på et eksempel med en hjelpeklasse:

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());
    }
}

Kontrollere statiske interaksjoner

Akkurat som med vanlige mocker kan du kontrollere interaksjoner med statiske metoder. Bruk metoden verify() på MockedStatic-objektet.

Dette sikrer at koden som testes, kalte den statiske metoden som forventet, med riktige argumenter og riktig antall ganger.

Legg merke til Mockito.times(1) i eksempelet ovenfor, som brukes til å kontrollere antall kall.

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
    }
}

Tøyle final-klasser

mockito-inline-mock maker gjør det også mulig å mocke final-klasser og final-metoder. Dette er spesielt nyttig når du arbeider med tredjepartsbiblioteker eller eldre kode der du ikke enkelt kan endre klassedesignet.

Du mocker final-klasser på samme måte som vanlige klasser: 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");
    }
}

Fange opp nye objekter

Hva om koden din oppretter nye objekter direkte ved hjelp av new MyObject()? MockedConstruction lar deg fange opp disse kallene og returnere mock-instanser i stedet for ekte objekter.

Dette er avgjørende når du tester klasser som håndterer avhengighetene sine internt, i stedet for å motta dem gjennom avhengighetsinjeksjon.

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
    }
}

Bruk med omtanke

Selv om mocking av statiske metoder og konstruktører er kraftig, kan det være et tegn på at designet er vanskelig å teste.

  • Designlukt: Omfattende bruk av slike mocker kan tyde på at klassene dine er for tett koblet eller ikke følger prinsippene for avhengighetsinjeksjon.
  • Lesbarhet: Tester som bruker disse avanserte mockene, kan være vanskeligere å forstå og vedlikeholde.
  • Når bør de brukes: De egner seg best for eldre kode, tredjepartsbiblioteker eller situasjoner der refaktorering ikke er mulig med en gang. Prioriter refaktorering for bedre testbarhet når det er mulig!

Kontroll av avansert mocking

Hvilke av følgende påstander om mockito-inline og avanserte mocking-teknikker er SANNE?

Oppsummering av avansert mocking

Godt jobbet! Du har utforsket avanserte Mockito-teknikker:

  • mockito-inline mock maker muliggjør mocking av statiske metoder, final-klasser og konstruktører.
  • MockedStatic lar deg stubbe og kontrollere kall til statiske metoder innenfor et definert område.
  • MockedConstruction hjelper deg med å fange opp og erstatte objekter som opprettes ved hjelp av nøkkelordet new.

Disse verktøyene er kraftige når du skal teste utfordrende kode, men husk å ta hensyn til de underliggende designmønstrene. Deretter skal du lære om beste praksis for Mockito!

Gratis å komme i gang

Lær deg Testing i praksis: JUnit, Mockito og integrasjonstester med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
12
Leksjoner
48

Ofte stilte spørsmål

Er leksjonen «Mocking av statiske metoder og konstruktører» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Testing i praksis: JUnit, Mockito og integrasjonstester, inkludert «Mocking av statiske metoder og konstruktører», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Testing i praksis: JUnit, Mockito og integrasjonstester inneholder totalt 4 leksjoner.

Hva lærer jeg i «Mocking av statiske metoder og konstruktører»?

Utforsk avanserte teknikker for å mocke statiske metoder, final-klasser og konstruktører ved hjelp av Mockito-utvidelser som `mockito-inline`. Du øver på Testing i praksis: JUnit, Mockito og integrasjonstester med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Testing i praksis: JUnit, Mockito og integrasjonstester?

Ingen tidligere erfaring er nødvendig. Testing i praksis: JUnit, Mockito og integrasjonstester på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.

Hvor lang tid tar leksjonen «Mocking av statiske metoder og konstruktører»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Testing i praksis: JUnit, Mockito og integrasjonstester-leksjonen?

Ja. Alle Testing i praksis: JUnit, Mockito og integrasjonstester-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Egendefinerte svar og tilbakekall
  2. Mocking av statiske metoder og konstruktører
  3. Beste praksis for Mockito
  4. Fangst av argumenter med ArgumentCaptor
← Tilbake til Testing i praksis: JUnit, Mockito og integrasjonstester