배경 & 문제점
- 바우처 관리 프로그램을 JAVA Spring 프레임워크로 구현하다가 Test Code 작성 중에 난관에 부딪혔다.
VoucherService
테스트 코드를 작성하는데Mockito
라이브러리를 이용해서 행위에 대한 테스트를 구현한다.
class VoucherServiceTest {
@Test
@DisplayName("voucher가 생성되어야 한다.")
void testCreateVoucher() {
// ...
}
}
Voucher
Class를 만드는VoucherFactory
클래스의 method인createVoucher()
가 정해진 인스턴스(fixedAmountVoucher
)를 반환하도록 mocking하려고 한다.
// VocherFactory.class
public class VoucherFactory {
private static final Logger logger = LoggerFactory.getLogger(VoucherFactory.class);
private static final VoucherFactory uniqueInstance = new VoucherFactory();
private VoucherFactory() {
}
public static VoucherFactory getInstance() {
return uniqueInstance;
}
public Voucher createVoucher(VoucherType type, UUID voucherId, long policyValue) {
switch (type) {
case PERCENTAGE:
return new PercentDiscountVoucher(voucherId, policyValue);
case FIXED:
return new FixedAmountVoucher(voucherId, policyValue);
default:
logger.error("Can't find voucher type");
throw new IllegalArgumentException();
}
}
}
// in test code
Voucher fixedAmountVoucher = new FixedAmountVoucher(UUID.randomUUID(), 100);
var voucherFactoryMock = mock(VoucherFactory.class);
- 에러 발생!
org.mockito.exceptions.base.MockitoException:
The used MockMaker SubclassByteBuddyMockMaker does not support the creation of static mocks
Mockito's inline mock maker supports static mocks based on the Instrumentation API.
You can simply enable this mock mode, by placing the 'mockito-inline' artifact where you are currently using 'mockito-core'.
Note that Mockito's inline mock maker is not supported on Android.
at org.prgrms.kdt.voucher.service.VoucherServiceTest.testCreateVoucher
원인 분석
VoucherFactory
Class는 인스턴스를 찍어내는 Factory Pattern이기 때문에 Singleton 으로 구현하였다.- 그래서
getInstance()
를 통해 유일한 자기 자신의 인스턴스를 반환하는데 이때 반환되는 값 또한 Mocking 되어야 한다. - 그리고 에러 로그를 살피던 중, static mock을 만들기 위해
mockito-inline
artifact가 필요하다고 한다.
해결
- 일단
mockito-inline
을 의존성에 추가한다.
// pom.xml
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>
- Static Method에 대해서도 mocking 하자.
Voucher fixedAmountVoucher = new FixedAmountVoucher(UUID.randomUUID(), 100);
// 1. static method를 위한 static mock 클래스를 만들어 주고
var voucherFactoryMockStatic = mockStatic(VoucherFactory.class);
// 2. static method가 아닌 method를 우린 사용하기 때문에 VoucherFactory에 대해 기본 mock 객체를 만들어 준다.
var voucherFactoryMock = mock(VoucherFactory.class);
// 3. static method mocking 해주기
voucherFactoryMockStatic
.when(VoucherFactory::getInstance)
.thenReturn(voucherFactoryMock);
// 4. 이제 우리가 사용해야할 createVoucher 메서드에 대해 모킹해주자.
when(voucherFactoryMock.createVoucher(
VoucherType.FIXED, fixedAmountVoucher.getVoucherId(), 100
)).thenReturn(fixedAmountVoucher);
느낀점
- 구글링도 구글링이지만, 구글링 전에 에러 로그들을 꼼꼼히 읽어보는 습관을 기르자...
- 테스트코드는 점점하면서 느는것 같다.
'👨🏻💻 Development > ☕️ Java' 카테고리의 다른 글
JUnit의 @BeforeClass 와 static block {} (0) | 2021.10.20 |
---|---|
2021년 09월 07일 TIL - 의존 역전 원칙 (Java) (0) | 2021.09.08 |
Java의 Object 클래스 (0) | 2021.08.07 |
[Java] String, StringBuffer, StringBuilder (0) | 2021.08.02 |