티스토리 뷰
[Error] kotlin으로 mockito의 Argument matchers 사용시 NullPointerException 발생
CharlieZip 2023. 7. 5. 19:59문제 상황
문제상황을 재현하기 위해 먼저 Person 클래스를 하나 생성하겠습니다.
class Person {
fun isDeveloper(name: String, age: Int): Boolean {
return true // 무조건 true를 반환
}
}
만약 isDeveloper()의 파라미터를 Stub할때 동적인 값을 허용할려면 아래과 같이 사용할 것입니다.
val mock = mock(Person::class.java)
`when`(mock.isDeveloper(anyString(), anyInt())).thenReturn(true)
여기서 추가로 name은 "charlie"이지만 age값은 동적으로 받고 싶은 경우에 문제가 발생합니다.
// NullPointerException 발생
`when`(mock.isDeveloper(eq("charlie"), anyInt())).thenReturn(false)
name 부분을 anyString에서 eq("charlie") 로 변경하니 NullPointerException 에러가 발생했습니다.
자세한 에러내용은 아래와 같았습니다.
java.lang.NullPointerException: eq("charlie") must not be null
왜 갑자기 에러가 발생하는지 이해할 수 없었지만 한가지 확실한거는 eq("charlie") 부분때문에 에러가 발생하는거는 알 수 있었습니다.
그래서 eq("charlie") 부분을 집중적으로 살펴보겠습니다.
문제 해결
먼저 NullPointerException 에러가 발생하는 원인을 찾기 위해 ArgumentMatchers의 eq() 메서드를 살펴보았습니다.
public static <T> T eq(T value) {
reportMatcher(new Equals(value));
if (value == null) return null;
return (T) Primitives.defaultValue(value.getClass());
}
eq() 메서드를 보니 if (value == null) return null 을 통해 null값을 다루고 있는것을 볼 수 있습니다.
현재 name필드는 null을 허용하기 않기 때문에 NullPointerException 에러가 발생한걸 확인할 수 있었습니다.
먼저 문제를 해결하기 위해 name 필드를 null을 허용하도록 변경하겠습니다.
class Person {
// String -> String? 변경
fun isDeveloper(name: String?, age: Int): Boolean {
return true
}
}
name필드를 String? 타입으로 변경하였더니 NullPointerException 에러가 더 이상 발생하지 않고 기존의 테스트 코드가 정상적으로 동작하게 됩니다.
발생하는 에러는 해결하였는데 한가지 찝찝한 부분이 있습니다.
테스트 코드를 동작시키기 위해 기존에 사용하던 Person 객체의 코드를 수정하게 된 부분입니다.
name필드를 null을 허용하도록 변경하므로써 테스트코드는 동작하였지만 이로 인해 name필드를 사용하는 다른 모든곳이 null처리를 해줘야 하며 null을 허용하므로써 다른곳에서 또다른 NullPointerException 에러가 발생할 취약점이 생겼습니다.
mockito-kotlin
그래서 기존 코드를 변경하지 않고 다른 방법으로 해당 문제를 해결해보겠습니다.
다른 방법은 mockito-kotlin 라이브러리를 사용하는 것입니다.
mokito-kotlin은 mockito를 kotlin으로 사용하기 좋게 한번더 랩핑시킨 라이브러리입니다.
mockito와 대부분 문법이 비슷하여 코드를 이해하는데 큰 어려움이 없으실 것입니다.
mockito-kotlin 라이브러리를 사용하기 위해서는 gradle에 아래 설정을 추가해야 합니다.
testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0")
Person 객체
class Person {
fun isDeveloper(name: String, age: Int): Boolean {
return true
}
}
Argument Matchers 사용
val mock = mock<Person>()
whenever(mock.isDeveloper(eq("charlie"), any())).thenReturn(false)
이렇게 mockito-kotlin의 Argument Matchers를 사용하면 NullPointerException이 발생하지 않고 정상적으로 코드가 동작하게 됩니다.
기존의 'when'을 mockito-kotlin에서는 whenever로 사용하는것을 제외하고는 코드도 동일합니다.
그러면 mockito와 동일하게 eq("charlie")를 사용하는데 왜 에러가 발생하지 않는지 알아보기 위해 똑같이 eq() 메서드를 살펴보겠습니다.
fun <T> eq(value: T): T {
return ArgumentMatchers.eq(value) ?: value
}
기존 mockito의 ArgumentMatchers.eq()를 한번 더 감싸서 null을 반환하는 경우 value값을 리턴해주면서 NullPointerException 에러가 발생하지 않게 처리해주는것을 확인할 수 있습니다.
kotlin을 사용시에는 kotlin문법을 지원해주는 mockito-kotlin을 사용하는게 언어적인 차이로 인해 발생하는 에러를 최대한 줄이면서 개발할 수 있을것 같습니다.
추가로 Kotest, Mockk와 같은 kotlin 테스트를 지원해주는 라이브러리가 있기 때문에 여러가지를 비교해보시고 사용해보시길 추천드립니다!
'기타' 카테고리의 다른 글
[Error] findByIdOrNull 사용시 WrongTypeOfReturnValue 에러 발생 (0) | 2023.06.22 |
---|---|
[Error] kotlin으로 mockito의 Argument matchers 사용시 NullPointerException 발생 (0) | 2023.04.27 |
[Intellij] Multiple Selector 단축키(같은 단어 일괄 선택) (0) | 2023.04.12 |
[Error] @DataJpaTest DataSource 설정 오류 (8) | 2021.12.19 |
[프로젝트] charlieZip #5 - bindingResult 메시지 Json 전송 (0) | 2021.11.21 |
- Total
- Today
- Yesterday
- Spring Batch
- java
- meta-data
- 클린 아키텍처
- IntelliJ
- autoconfigure
- Spring
- Collection
- Stream
- test
- asSequence
- mockito-kotlin
- Parameterized
- AWS INDUSTRY WEEK
- WrongTypeOfReturnValue
- prinicipal
- mockK
- assertj
- GSLB
- spring data jpa
- scenario test
- ExitStatus
- 시나리오 테스트
- kotlin
- A레코드
- Mockito
- datasource
- BatchStatus
- trailing comma
- JUnit5
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |