[Java] record class Mocking 에러 해결하기

2022. 12. 2. 03:13Java/Basic

반응형

record class로 작성된 Notice 객체를 모킹하려고 할때

public record NoticeDetail(long id, String title, String content, String createdAt) { }
class GetNoticeServiceTest {

    private final LoadNoticePort loadNoticePort = Mockito.mock(LoadNoticePort.class);

    @Test
    void getNotice() {
        Notice notice = givenNoticeWithId(1L);
        System.out.println(notice);
    }

    private Notice givenNoticeWithId(Long id) {
        Notice notice = Mockito.mock(Notice.class);
        given(notice.id()).willReturn(id);
        given(loadNoticePort.loadNotice(id)).willReturn(notice);
        return notice;
    }
}

아래와 같은 에러가 발생됩니다.

Cannot mock/spy class com.chaeking.api.notice.domain.Notice
Mockito cannot mock/spy because :
 - final class
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class com.chaeking.api.notice.domain.Notice
Mockito cannot mock/spy because :
 - final class
	at app//com.chaeking.api.notice.application.service.GetNoticeServiceTest.givenNoticeWithId(GetNoticeServiceTest.java:23)
	at app//com.chaeking.api.notice.application.service.GetNoticeServiceTest.getNotice(GetNoticeServiceTest.java:18)
	at java.base@17.0.4.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@17.0.4.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base@17.0.4.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@17.0.4.1/java.lang.reflect.Method.invoke(Method.java:568)
	at app//org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)

기본적으로 Mockito 는 final class를 모킹할 수 없는데,
record class는 불변 데이터 객체이기 때문에 모킹이 되지 않는 것입니다.


해결책 1. final을 붙이지 않은 클래스로 변경하기

Notice 클래스를 final을 붙이지 않은 일반 클래스로 변경합니다.

@AllArgsConstructor
@Data
public class Notice {
    private Long id;
    private String title;
    private String content;
    private LocalDateTime createdAt;
}
class GetNoticeServiceTest {

    private final LoadNoticePort loadNoticePort = Mockito.mock(LoadNoticePort.class);

    @Test
    void getNotice() {
        Notice notice = givenNoticeWithId(1L);
        System.out.println(notice);
    }

    private Notice givenNoticeWithId(Long id) {
        Notice notice = Mockito.mock(Notice.class);
        given(notice.getId()).willReturn(id);
        given(loadNoticePort.loadNotice(id)).willReturn(notice);
        return notice;
    }
}

일반 클래스로 변경하니 아래와 같이 모킹이 정상적으로 작동되었습니다.

> Task :chaeking-api-hexagon:compileJava UP-TO-DATE
> Task :chaeking-api-hexagon:processResources UP-TO-DATE
> Task :chaeking-api-hexagon:classes UP-TO-DATE
> Task :chaeking-api-hexagon:compileTestJava
> Task :chaeking-api-hexagon:processTestResources UP-TO-DATE
> Task :chaeking-api-hexagon:testClasses
> Task :chaeking-api-hexagon:test
Mock for Notice, hashCode: 1227459815
BUILD SUCCESSFUL in 931ms
5 actionable tasks: 2 executed, 3 up-to-date
3:00:03 AM: Execution finished ':chaeking-api-hexagon:test --tests "com.chaeking.api.notice.application.service.GetNoticeServiceTest.getNotice"'.

해결책 2. Mockito inline 의존성을 추가하기

final class, final method, static method의 모킹을 지원해주는 Mockito inline 의존성을 추가해주면 기존 코드가 정상적으로 돌아갑니다.

testImplementation("org.mockito:mockito-inline:4.9.0")

위의 의존성을 추가하면, Notice 클래스를 record 클래스로 둬도 모킹이 정상적으로 잘 동작됩니다.

> Task :chaeking-api-hexagon:compileJava UP-TO-DATE
> Task :chaeking-api-hexagon:processResources UP-TO-DATE
> Task :chaeking-api-hexagon:classes UP-TO-DATE
> Task :chaeking-api-hexagon:compileTestJava UP-TO-DATE
> Task :chaeking-api-hexagon:processTestResources UP-TO-DATE
> Task :chaeking-api-hexagon:testClasses UP-TO-DATE
> Task :chaeking-api-hexagon:test
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
Mock for Notice, hashCode: 184244257
BUILD SUCCESSFUL in 1s
5 actionable tasks: 1 executed, 4 up-to-date
3:04:00 AM: Execution finished ':chaeking-api-hexagon:test --tests "com.chaeking.api.notice.application.service.GetNoticeServiceTest"'.

++

  • how to mock a record class
  • record class 모킹하기
  • final class 모킹하기
728x90
반응형