0Pricing
Testing Mastery: JUnit, Mockito & Integration Tests · 강의

사용자 지정 응답과 콜백

`Answer` 인터페이스를 사용하여 모의 메서드 호출에 대한 사용자 지정 로직을 구현하고 복잡한 동작을 시뮬레이션합니다.

사용자 지정 응답과 콜백은(는) CoddyKit의 무료 Testing Mastery: JUnit, Mockito & Integration Tests 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Testing Mastery: JUnit, Mockito & Integration Tests 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Testing Mastery: JUnit, Mockito & Integration Tests 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

Beyond Simple Stubbing

Sometimes, simply returning a fixed value with when().thenReturn() isn't enough for your mock objects.

  • What if a mock method needs to modify an argument passed to it?
  • What if it needs to execute a callback function?
  • What if its return value depends on multiple inputs in a complex, dynamic way?

These advanced scenarios call for custom logic within your mocks.

Introducing Mockito's Answer

Mockito provides the org.mockito.stubbing.Answer interface. This powerful tool lets you define custom behavior for a mocked method call.

  • Think of it as writing a small piece of code that runs whenever the mocked method is invoked.
  • The Answer interface has a single method: Object answer(Invocation invocation) throws Throwable;.

It gives you fine-grained control over mock responses.

Anatomy of answer()

The answer() method is where you implement your custom logic. It receives an InvocationOnMock object (often just referred to as invocation).

  • The InvocationOnMock object provides crucial details about the method call:
    • Which method was called?
    • What arguments were passed?
    • Which mock object was involved?

You can use these details to decide what to return, throw an exception, or perform side effects.

Using doAnswer for Flexibility

While you can use when().thenAnswer(), Mockito's doAnswer() method is often more flexible and widely used, especially for void methods or when combining with other stubbings.

  • doAnswer() allows you to specify behavior for a method call before defining when().
  • It's particularly useful when you need to:
    • Capture arguments.
    • Invoke callbacks.
    • Change the state of objects passed as arguments.

Custom Return Logic

Let's see how to use doAnswer to return a value that depends on the input argument. Here, our mock will double the input number.

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

Simulating Callbacks

A very common and powerful use case for doAnswer is simulating asynchronous operations or invoking callbacks.

  • Imagine a service that takes a Callback interface (e.g., a Consumer) as an argument.
  • You can use doAnswer to manually invoke the onSuccess or onFailure method of that callback.

This allows you to test how your application code reacts to different callback outcomes without actual asynchronous execution.

Mocking a Callback

Here, we simulate a DataService calling a Consumer callback. doAnswer lets us trigger the callback with a specific value when fetchData is called.

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.");
    }
}

Deeper with Invocation

The InvocationOnMock object (or simply invocation) passed to your answer() method is a powerful context object.

  • invocation.getArguments(): Returns an array of all arguments passed to the mocked method.
  • invocation.getArgument(index): Returns a specific argument by its index.
  • invocation.getMethod(): Gives you the Method object that was called, allowing reflection.
  • invocation.getMock(): Returns the mock object itself.
  • invocation.callRealMethod(): For spies, this allows invoking the actual, real method.

Tips for Answer Use

Custom answers are powerful, but it's important to use them thoughtfully to keep your tests maintainable and clear:

  • Keep them simple: If your answer logic becomes too complex, it might indicate a design issue in your production code or an overly complicated test setup.
  • Prefer simpler stubbing: Always choose simpler stubbing methods like thenReturn(), thenThrow(), or thenCallRealMethod() if they can achieve the desired behavior.
  • Test the Answer itself: For very complex Answer implementations, consider extracting them into a separate class and writing a unit test for that class.

When to Use Answer

Here are typical situations where the Answer interface (usually via doAnswer()) truly shines:

  • Dynamic return values: When the return value depends on input arguments in a non-trivial or calculated way.
  • Side effects: Modifying arguments that are passed by reference (e.g., updating an object).
  • Callbacks: Triggering success or failure callbacks to simulate asynchronous operations.
  • Chaining operations: Simulating a sequence of interactions or state changes within a single mock call.

Custom Mock Behavior

You are testing a UserService that depends on a UserRepository. The UserRepository has a method save(User user) which typically updates the user's ID after saving it to a database.

You want to mock UserRepository to simulate this behavior without hitting a real database. Assume a User class with setId(Long id).

Recap: Dynamic Mocks

You've learned how to bring dynamic and complex behavior to your mocks using Mockito's Answer interface and the doAnswer method.

  • The Answer interface lets you define custom logic that executes when a mocked method is called.
  • The InvocationOnMock object provides essential context, including method arguments and the mock itself.
  • doAnswer is ideal for dynamic return values, simulating callbacks, or modifying arguments passed by reference.

Remember to use `Answer` judiciously, preferring simpler stubbing when possible, to keep your tests clear and focused.

자주 묻는 질문

“사용자 지정 응답과 콜백” 강의는 무료인가요?

네 — “사용자 지정 응답과 콜백” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Testing Mastery: JUnit, Mockito & Integration Tests 강의 전체를 잠금 해제할 수 있습니다. Testing Mastery: JUnit, Mockito & Integration Tests 강의에는 총 4개의 강의가 포함되어 있습니다.

“사용자 지정 응답과 콜백”에서 뭘 배우나요?

`Answer` 인터페이스를 사용하여 모의 메서드 호출에 대한 사용자 지정 로직을 구현하고 복잡한 동작을 시뮬레이션합니다. 브라우저에서 직접 실행하는 실습 코드로 Testing Mastery: JUnit, Mockito & Integration Tests을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Testing Mastery: JUnit, Mockito & Integration Tests을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Testing Mastery: JUnit, Mockito & Integration Tests은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“사용자 지정 응답과 콜백” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Testing Mastery: JUnit, Mockito & Integration Tests 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Testing Mastery: JUnit, Mockito & Integration Tests 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 사용자 지정 응답과 콜백
  2. 정적 메서드와 생성자 모의 처리
  3. Mockito 모범 사례
  4. ArgumentCaptor로 인수 캡처하기
← Testing Mastery: JUnit, Mockito & Integration Tests(으)로 돌아가기