일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 파이썬 소스
- 리눅스
- 백준 알고리즘
- resilience4j
- c#
- hyperledger
- 초대장
- 자바 프로젝트
- 자바
- JVM
- gradle
- SQL
- MongoDB
- 운영체제
- 유사코드
- spring
- 오라클 디비
- 알고리즘
- K6
- smart cast
- oracle
- 티스토리
- auto configure
- dynamic query
- jsp
- 오라클
- 문법 정리
- 학점
- 파이썬
- 프로젝트
Archives
- Today
- Total
모종닷컴
테스트 코드에서 클래스 생성에 필요한 모든 클래스를 mocking 본문
반응형
진행하고 있는 프로젝트가 슬슬 마무리가 되어가네요.. 포스팅할 거리가 많은데 프로젝트가 끝나면 하나씩 정리해서 올리도록 하겠습니다. 너무 오랫동안 글을 못 올리는 게 싫어서 짧은 글이지만 소개해보려고 합니다.
의존하고 있는 빈이 많은 클래스들이 간혹 보입니다. 설계를 잘못한걸수도 있지만 이런 클래스들은 특히 단위 테스트 코드를 짤 때 정말 귀찮습니다. 예시를 보도록 하겠습니다.
예시 코드
private val log = KotlinLogging.logger { }
@Service
class WelcomeStep {
fun execute() {
log.info("welcome")
}
}
@Service
class EnjoyStep {
fun execute() {
log.info("enjoy")
}
}
@Service
class GoodByeStep {
fun execute() {
log.info("good byte")
}
}
(인터페이스로 묶고 싶은 욕망은 잠시 넣어두세요)
@Service
class GameService(
private val welcomeStep: WelcomeStep,
private val enjoyStep: EnjoyStep,
private val goodByeStep: GoodByeStep
) {
fun process() {
welcomeStep.execute()
enjoyStep.execute()
goodByeStep.execute()
}
}
테스트 코드
@RunWith(SpringJUnit4ClassRunner::class)
internal class GameServiceV1Test {
@InjectMocks
private lateinit var gameService: GameService
private val welcomeStep: WelcomeStep = spy { }
private val enjoyStep: EnjoyStep = spy { }
private val goodByeStep: GoodByeStep = spy { }
@Test
fun test() {
gameService.process()
}
}
mockito-kotlin 라이브러리를 사용하였습니다.
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
위와 같이 테스트 코드를 짜볼수 있습니다. 여기서 저는 각각의 필요한 빈들을 매번 저런 식으로 넣어주어야 하는 게 귀찮았습니다.
예를 들어 GameService에 RestStep 이 추가된다 가정한다면 추후에 개발 중간 중간 테스트 코드가 아래의 예시와 같이 깨지는 것도 볼 수 있습니다.
restStep 추가
@Service
class GameService(
private val welcomeStep: WelcomeStep,
private val enjoyStep: EnjoyStep,
private val goodByeStep: GoodByeStep,
private val restStep: RestStep
) {
fun process() {
welcomeStep.execute()
enjoyStep.execute()
restStep.execute()
goodByeStep.execute()
}
}
Test Fail
Parameter specified as non-null is null: method GameService.<init>, parameter restStep
테스트코드 필드에 아래를 추가해주면 테스트가 통과됩니다.
private val restStep: RestStep = spy { }
이렇게 매 의존 클래스가 늘어갈 때마다 테스트 코드가 계속 수정되어야 합니다. 그래서 이런 귀찮음을 줄이고자 생성에 필요한 모든 클래스를 mock으로 만들어 주입해버리면 어떨까 싶었습니다.
생성자에 필요한 모든 클래스를 mock으로 만들어 생성
internal class GameServiceTest {
private lateinit var gameService: GameService
@Before
fun getRequiredBean() {
val constructor = GameService::class.java.constructors[0]
// 생성에 필요한 클래스를 모두 mock으로 만들어 주입.
val mockBeans = constructor.parameterTypes.map { Mockito.spy(it) }
gameService = constructor.newInstance(*mockBeans.toTypedArray()) as GameService
}
@Test
fun test() {
gameService.process()
}
}
이렇게 하면 클래스가 매번 늘어나더라도 코드의 수정이 필요하지 않습니다. 그리고 주입된 클래스가 필요한 경우에는 아래와 같은 방식으로 가져오면 됩니다.
@Test
fun test() {
val restStep = ReflectionTestUtils.getField(gameService, "restStep") as RestStep
whenever(restStep.execute()).thenAnswer { println("mock rest time..") }
gameService.process()
}
반응형
'Programming > Spring' 카테고리의 다른 글
Circuit Breaker Pattern 그리고 이를 스프링에 적용해보기 (0) | 2022.11.12 |
---|---|
Spring MongoDB Transaction Support (0) | 2022.10.23 |
멀티 모듈 프로젝트에서는 아카이브 이름도 조심해야 합니다. (0) | 2022.09.24 |
멀티 모듈 프로젝트에서 다중 프로퍼티 파일 다루기 (0) | 2022.09.11 |
Mock Layer (0) | 2022.08.17 |