Testning på avancerad nivå: JUnit, Mockito och integrationstester · Lektion

Anpassade svar och callbacks

Implementera anpassad logik för anrop av mockade metoder med hjälp av `Answer`-gränssnitt för att simulera komplexa beteenden.

Lektion 1 av 412 steg

Anpassade svar och callbacks är en gratis lektion i Testning på avancerad nivå: JUnit, Mockito och integrationstester på CoddyKit. Detta är lektion 1 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Testning på avancerad nivå: JUnit, Mockito och integrationstester, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Testning på avancerad nivå: JUnit, Mockito och integrationstester innehåller totalt 4 lektioner.

Bortom enkel stubbing

Ibland räcker det inte att helt enkelt returnera ett fast värde med when().thenReturn() för dina mockobjekt.

  • Vad händer om en mockmetod behöver ändra ett argument som skickas till den?
  • Vad händer om den behöver köra en callback-funktion?
  • Vad händer om returvärdet på ett komplext och dynamiskt sätt beror på flera indata?

Dessa avancerade scenarier kräver anpassad logik i dina mockar.

Introduktion till Mockitos Answer

Mockito tillhandahåller gränssnittet org.mockito.stubbing.Answer. Med detta kraftfulla verktyg kan du definiera anpassat beteende för ett anrop av en mockad metod.

  • Se det som att du skriver en liten koddel som körs varje gång den mockade metoden anropas.
  • Gränssnittet Answer har en enda metod: Object answer(Invocation invocation) throws Throwable;.

Det ger dig detaljerad kontroll över mockens svar.

Anatomin hos answer()

Metoden answer() är platsen där du implementerar din anpassade logik. Den tar emot ett InvocationOnMock-objekt, som ofta bara kallas invocation.

  • Objektet InvocationOnMock tillhandahåller viktig information om metodanropet:
    • Vilken metod anropades?
    • Vilka argument skickades med?
    • Vilket mockobjekt användes?

Du kan använda denna information för att avgöra vad som ska returneras, om ett undantag ska kastas eller om sidoeffekter ska utföras.

Använda doAnswer för flexibilitet

Även om du kan använda when().thenAnswer() är Mockitos metod doAnswer() ofta mer flexibel och används i större utsträckning, särskilt för void-metoder eller när den kombineras med annan stubbning.

  • Med doAnswer() kan du ange beteendet för ett metodanrop innan du definierar when().
  • Det är särskilt användbart när du behöver:
    • Fånga argument.
    • Anropa callbacks.
    • Ändra tillståndet hos objekt som skickas som argument.

Anpassad returvärdeslogik

Vi ska se hur du använder doAnswer för att returnera ett värde som beror på indataargumentet. Här kommer vår mock att dubbla indatan.

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

// A simple interface to mock
interface Calculator {
    int calculate(int input);
}

public class Main {
    public static void main(String[] args) {
        // Create a mock object for our Calculator interface
        Calculator mockCalculator = Mockito.mock(Calculator.class);

        // Define custom answer to double the input argument
        Mockito.doAnswer(new Answer<Integer>() {
            @Override
            public Integer answer(InvocationOnMock invocation) throws Throwable {
                // Get the first argument (index 0) as an Integer
                Integer arg = invocation.getArgument(0);
                return arg * 2; // Return double the argument
            }
        }).when(mockCalculator).calculate(Mockito.anyInt()); // Apply to any int input

        // Test the custom behavior of the mock
        System.out.println("Result for 5: " + mockCalculator.calculate(5));
        System.out.println("Result for 10: " + mockCalculator.calculate(10));
    }
}

Simulera callbacks

Ett mycket vanligt och kraftfullt användningsområde för doAnswer är att simulera asynkrona operationer eller anropa callbacks.

  • Föreställ dig en tjänst som tar ett Callback-gränssnitt, till exempel en Consumer, som argument.
  • Du kan använda doAnswer för att manuellt anropa callbackens metod onSuccess eller onFailure.

På så sätt kan du testa hur applikationskoden reagerar på olika callbackresultat utan att faktiskt köra asynkront.

Mocka en callback

Här simulerar vi att DataService anropar en Consumer-callback. Med doAnswer kan vi utlösa callbacken med ett specifikt värde när fetchData anropas.

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.function.Consumer;

// An interface representing a service that fetches data asynchronously
interface DataService {
    void fetchData(String query, Consumer<String> callback);
}

public class Main {
    public static void main(String[] args) {
        DataService mockService = Mockito.mock(DataService.class);

        // Configure the mock to simulate fetching data and calling the callback
        Mockito.doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
                // Get the arguments passed to fetchData
                String query = invocation.getArgument(0); // The first argument is the query
                Consumer<String> callback = invocation.getArgument(1); // The second is the callback
                
                // Simulate a successful data fetch by invoking the callback
                callback.accept("Data for '" + query + "' fetched successfully!");
                return null; // Void methods in doAnswer return null
            }
        }).when(mockService).fetchData(Mockito.anyString(), Mockito.any(Consumer.class));

        // Our application code would typically use this service
        System.out.println("Application requesting user data...");
        mockService.fetchData("user:456", result -> {
            System.out.println("Application received data: " + result);
        });
        System.out.println("Application finished request setup.");
    }
}

Fördjupa dig i Invocation

Objektet InvocationOnMock, eller helt enkelt invocation, som skickas till metoden answer() är ett kraftfullt kontextobjekt.

  • invocation.getArguments(): Returnerar en array med alla argument som skickades till den mockade metoden.
  • invocation.getArgument(index): Returnerar ett specifikt argument utifrån dess index.
  • invocation.getMethod(): Ger dig det Method-objekt som anropades, vilket möjliggör reflection.
  • invocation.getMock(): Returnerar själva mockobjektet.
  • invocation.callRealMethod(): För spies gör detta det möjligt att anropa den faktiska metoden.

Tips för användning av Answer

Anpassade Answer-implementationer är kraftfulla, men det är viktigt att använda dem genomtänkt för att hålla testerna lättunderhållna och tydliga:

  • Håll dem enkla: Om logiken i din answer blir för komplex kan det tyda på ett designproblem i produktionskoden eller en alltför komplicerad testkonfiguration.
  • Föredra enklare stubbning: Välj alltid enklare stubbningsmetoder som thenReturn(), thenThrow() eller thenCallRealMethod() om de kan ge det önskade beteendet.
  • Testa själva Answer: För mycket komplexa Answer-implementationer kan du överväga att extrahera dem till en separat klass och skriva ett enhetstest för den klassen.

När ska Answer användas

Här är typiska situationer där gränssnittet Answer, vanligtvis via doAnswer(), verkligen kommer till sin rätt:

  • Dynamiska returvärden: När returvärdet beror på indataargument på ett icke-trivialt eller beräknat sätt.
  • Sidoeffekter: När argument som skickas med referens ändras, till exempel när ett objekt uppdateras.
  • Callbacks: När lyckade eller misslyckade callbacks utlöses för att simulera asynkrona operationer.
  • Kedja operationer: När en sekvens av interaktioner eller tillståndsändringar simuleras inom ett enda mockanrop.

Anpassat mockbeteende

Du testar en UserService som är beroende av en UserRepository. UserRepository har en metod save(User user) som vanligtvis uppdaterar användarens ID efter att användaren har sparats i en databas.

Du vill mocka UserRepository för att simulera detta beteende utan att ansluta till en riktig databas. Anta att det finns en User-klass med setId(Long id).

Sammanfattning: Dynamiska mockar

Du har lärt dig hur du lägger till dynamiskt och komplext beteende i dina mockar med hjälp av Mockitos gränssnitt Answer och metoden doAnswer.

  • Med gränssnittet Answer kan du definiera anpassad logik som körs när en mockad metod anropas.
  • Objektet InvocationOnMock tillhandahåller viktig kontext, bland annat metodargumenten och själva mockobjektet.
  • doAnswer passar utmärkt för dynamiska returvärden, simulering av callbacks eller ändring av argument som skickas med referens.

Kom ihåg att använda `Answer` med omdöme och föredra enklare stubbning när det är möjligt, så att testerna förblir tydliga och fokuserade.

Gratis att börja

Lär dig Testning på avancerad nivå: JUnit, Mockito och integrationstester med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
12
Lektioner
48

Vanliga frågor

Är lektionen ”Anpassade svar och callbacks” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Testning på avancerad nivå: JUnit, Mockito och integrationstester, inklusive ”Anpassade svar och callbacks”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Testning på avancerad nivå: JUnit, Mockito och integrationstester innehåller totalt 4 lektioner.

Vad lär jag mig i ”Anpassade svar och callbacks”?

Implementera anpassad logik för anrop av mockade metoder med hjälp av `Answer`-gränssnitt för att simulera komplexa beteenden. Ni övar på Testning på avancerad nivå: JUnit, Mockito och integrationstester med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Testning på avancerad nivå: JUnit, Mockito och integrationstester?

Du behöver inga förkunskaper. Utbildningen i Testning på avancerad nivå: JUnit, Mockito och integrationstester på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.

Hur lång tid tar lektionen ”Anpassade svar och callbacks”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Testning på avancerad nivå: JUnit, Mockito och integrationstester-lektionen?

Ja. Varje Testning på avancerad nivå: JUnit, Mockito och integrationstester-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Anpassade svar och callbacks
  2. Mockning av statiska metoder och konstruktorer
  3. Bästa praxis för Mockito
  4. Fånga argument med ArgumentCaptor
← Tillbaka till Testning på avancerad nivå: JUnit, Mockito och integrationstester