Mestrer test: JUnit, Mockito og integrationstest · Lektion

Mocking af statiske metoder og konstruktører

Udforsk avancerede teknikker til at mocke statiske metoder, final-klasser og konstruktører ved hjælp af Mockito-udvidelser som `mockito-inline`.

Lektion 2 af 411 trin

Mocking af statiske metoder og konstruktører er en gratis Mestrer test: JUnit, Mockito og integrationstest-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Mestrer test: JUnit, Mockito og integrationstest, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Mestrer test: JUnit, Mockito og integrationstest-kurset indeholder 4 lektioner i alt.

Ud over almindelige mocks

Velkommen! Indtil videre har du lært at mocke interfaces og almindelige klasser med Mockito. Men hvad med de vanskelige dele af koden, der virker modstandsdygtige over for test?

Nogle gange møder du statiske metoder, final-klasser eller direkte konstruktørkald (new MyObject()) i den kode, du vil teste. Standard-Mockito kan ikke håndtere disse direkte.

Når traditionelle mocks ikke slår til

Hvorfor har vi brug for at mocke disse?

  • Statiske hjælpe­metoder: Bruges ofte til almindelige opgaver, men er svære at isolere, hvis de har sideeffekter eller eksterne afhængigheder.
  • Final-klasser/metoder: Kan ikke nedarves fra eller tilsidesættes, hvilket gør traditionel proxy-baseret mocking umulig.
  • Konstruktørkald (new): Direkte oprettelse af objekter i en metode gør det svært at indsætte en mock i stedet.

Mocking af disse elementer hjælper dig med at isolere den kode, der testes, selv i komplekse eller ældre systemer.

Giv Mockito flere kræfter

For at overvinde disse begrænsninger tilbyder Mockito avancerede funktioner gennem sin inline mock maker, der ofte omtales som mockito-inline.

Denne funktion bruger bytekode-manipulation til at "omskrive" klasser under kørsel, så Mockito kan mocke ting, som det normalt ikke kan, såsom statiske metoder, final-klasser og endda konstruktører.

Det er et effektivt værktøj, men brug det med omtanke!

Opsætning af dit projekt

I Mockito-version 3.4.0 og nyere er inline mock maker ofte aktiveret som standard eller tilgængelig blot ved at bruge standardafhængigheden mockito-core.

Hvis du bruger en ældre version eller støder på problemer, kan det være nødvendigt at deklarere mockito-inline eksplicit. I de fleste moderne opsætninger skal du blot sikre dig, at du har en nyere version af mockito-core.

Sådan kan din build.gradle se ud:

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 af statiske hjælpere

Til mocking af statiske metoder leverer Mockito interfacet MockedStatic. Du opretter en mock-kontekst med Mockito.mockStatic() i en try-with-resources-blok. Det sikrer, at mocken kun er aktiv, så længe denne blok kører.

Lad os se et eksempel med en hjælpeklasse:

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

Kontrol af statiske interaktioner

Ligesom med almindelige mocks kan du verificere interaktioner med statiske metoder. Brug metoden verify() på dit MockedStatic-objekt.

Det sikrer, at den kode, der testes, kaldte den statiske metode som forventet med de korrekte argumenter og det korrekte antal gange.

Bemærk Mockito.times(1) i det foregående eksempel, som kontrollerer antallet af kald.

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æmning af final-klasser

Mock maker-komponenten mockito-inline lader dig også mocke final-klasser og final-metoder. Det er især nyttigt ved arbejde med tredjepartsbiblioteker eller ældre kode, hvor du ikke nemt kan ændre klassens design.

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

Opsnapning af nye objekter

Hvad nu, hvis din kode opretter nye objekter direkte med new MyObject()? MockedConstruction lader dig opsnappe disse kald og returnere mock-instanser i stedet for rigtige objekter.

Det er afgørende, når du tester klasser, der selv håndterer deres afhængigheder i stedet for at modtage dem gennem dependency injection.

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

Brug det med omtanke

Selvom mocking af statiske metoder og konstruktører er effektivt, kan det være et tegn på et design, der er svært at teste.

  • Designproblem: En stor afhængighed af disse mocks kan tyde på, at dine klasser er for tæt koblede eller ikke følger principperne for dependency injection.
  • Læselighed: Tests, der bruger disse avancerede mocks, kan være sværere at forstå og vedligeholde.
  • Hvornår skal det bruges: Det er bedst egnet til ældre kode, tredjepartsbiblioteker eller situationer, hvor refaktorering ikke er mulig med det samme. Prioritér refaktorering med henblik på testbarhed, når det er muligt!

Kontrol af avanceret mocking

Hvilke af følgende udsagn om mockito-inline og avancerede mocking-teknikker er SANDE?

Opsummering af avanceret mocking

Godt klaret! Du har udforsket avancerede Mockito-teknikker:

  • Mock maker-komponenten mockito-inline gør det muligt at mocke statiske metoder, final-klasser og konstruktører.
  • MockedStatic lader dig oprette stubs for og verificere kald af statiske metoder inden for et defineret scope.
  • MockedConstruction hjælper med at opsnappe og erstatte objekter, der oprettes med nøgleordet new.

Disse værktøjer er effektive til test af vanskelig kode, men husk at overveje de underliggende designmønstre. Nu skal du lære om bedste praksis for Mockito!

Gratis at komme i gang

Lær Mestrer test: JUnit, Mockito og integrationstest med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
12
Lektioner
48

Ofte stillede spørgsmål

Er lektionen “Mocking af statiske metoder og konstruktører” gratis?

Ja — alle 3 lektioner i læringssporet Mestrer test: JUnit, Mockito og integrationstest, inklusive “Mocking af statiske metoder og konstruktører”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Mestrer test: JUnit, Mockito og integrationstest-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Mocking af statiske metoder og konstruktører”?

Udforsk avancerede teknikker til at mocke statiske metoder, final-klasser og konstruktører ved hjælp af Mockito-udvidelser som `mockito-inline`. Du øver dig i Mestrer test: JUnit, Mockito og integrationstest med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Mestrer test: JUnit, Mockito og integrationstest?

Der kræves ingen tidligere erfaring. Mestrer test: JUnit, Mockito og integrationstest på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “Mocking af statiske metoder og konstruktører”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Mestrer test: JUnit, Mockito og integrationstest-lektion?

Ja. Alle Mestrer test: JUnit, Mockito og integrationstest-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Brugerdefinerede svar og callbacks
  2. Mocking af statiske metoder og konstruktører
  3. Bedste praksis for Mockito
  4. Indfangning af argumenter med ArgumentCaptor
← Tilbage til Mestrer test: JUnit, Mockito og integrationstest