[java] Mockito를 사용하여 특정 메소드에 전달된 인자들로 다른 메소드를 호출할 때 실행 중인 스레드를 모킹하는 방법은?
  1. Mockito를 프로젝트에 추가합니다. Maven을 사용한다면 pom.xml에 다음 의존성을 추가하세요.
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.8.0</version>
    <scope>test</scope>
</dependency>
  1. 실행 중인 스레드를 모킹하기 위해 Thread 클래스를 확장한 모킹 클래스를 작성합니다.
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class MockThread extends Thread {
    private static final Thread MAIN_THREAD = new MockThread();

    public static void mockCurrentThread() {
        Mockito.mockStatic(Thread.class);
        Mockito.when(Thread.currentThread()).thenReturn(MAIN_THREAD);
        Mockito.when(Thread.currentThread().isAlive()).thenReturn(true);
        Mockito.when(Thread.currentThread().getName()).thenReturn("main");
    }

    public static void cleanupMock() {
        Mockito.mockStatic(Thread.class).close();
    }

    public static void resetMock() {
        Mockito.reset(MAIN_THREAD);
    }

    @Override
    public void run() {
        // 꼭 필요한 경우에만 오버라이드
        super.run();
    }

    @Override
    public String toString() {
        return MAIN_THREAD.toString();
    }

    @Override
    public int hashCode() {
        return MAIN_THREAD.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return MAIN_THREAD.equals(obj);
    }

    // 특정 메소드 호출 시 원하는 결과를 반환하는 예제
    public static Answer<?> answer(Object expectedValue) {
        return new Answer<Object>() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                return expectedValue;
            }
        };
    }
}
  1. 테스트를 작성할 클래스에서 MockThread.mockCurrentThread()를 호출하여 특정 메소드를 호출하는 부분을 모킹하고 스레드를 실행 중인 것처럼 동작하도록 만들 수 있습니다.
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

public class MyTestClass {
    @BeforeEach
    public void setup() {
        MockThread.mockCurrentThread();
    }

    @AfterEach
    public void cleanup() {
        MockThread.cleanupMock();
    }

    @Test
    public void testMyMethod() {
        // Arrange
        MyObject myObject = mock(MyObject.class);
        doAnswer(MockThread.answer(42)).when(myObject).myMethod(anyString());

        // Act
        // 실행 중인 스레드인 것처럼 동작하도록 호출
        myObject.myMethod("test");

        // Assert
        // 모킹한 메소드가 실행되었는지 확인
        verify(myObject).myMethod(anyString());

        // 원하는 결과를 반환하는지 확인
        assertEquals(42, myObject.getResult());
    }
}

위의 예시 코드에서 MyObject는 특정 메소드를 갖는 클래스입니다. MyObjectmyMethod()가 호출될 때, Thread.currentThread()를 호출하더라도 MockThread.MAIN_THREAD를 반환하도록 모킹되어 있습니다. 이를 통해 실행 중인 스레드를 모킹할 수 있습니다.

테스트 코드에서는 MyObjectmock()으로 모킹한 뒤, doAnswer()를 사용하여 myMethod()가 호출되었을 때 원하는 결과를 반환하도록 설정하였습니다. 이후에는 myObject에서 myMethod()를 호출하고, 원하는 결과를 반환하는지 확인하는 테스트를 수행하였습니다.

추가로, MockThread 클래스에는 스레드와 관련된 다른 메소드들을 모킹한 예제도 포함되어 있습니다. 이를 통해 필요한 경우에 다양한 스레드 관련 동작을 모킹할 수 있습니다.

테스트 코드에서 @BeforeEach@AfterEach 어노테이션을 사용하여 테스트 메소드를 실행하기 전과 후에 스레드 모킹과 관련된 초기화와 클린업을 수행하도록 했습니다.

이렇게 Mockito와 MockThread를 사용하여 특정 메소드에 전달된 인자들로 다른 메소드를 호출할 때 실행 중인 스레드를 모킹할 수 있습니다.

참고 자료