스프링부트로 회원가입에 필요한 휴대폰 인증 서비스에 대한 테스트를 작성하면서 알게된 점을 기록한 포스트입니다.
단위 테스트는 빨라야 한다는 원칙이다. 스프링부트에서 테스트를 위해 @SpringBootTest
어노테이션을 사용하면 스프링 컨테이너에 등록된 모든 빈이 로드된다. 이는 테스트의 무거움을 불러오고 테스트의 속도를 저하시킨다.
Fast 원칙에 위배되기 때문에 스프링부트에서 @SpringBootTest
어노테이션 자체가 통합테스트를 위한 어노테이션이긴 하지만 @SpringBootTest
어노테이션을 통한 테스트는 단위 테스트라 할 수 없다.
테스트가 다른 테스트 혹은 외부 소스등 통제할 수 없는 외부 상황에 의존할 경우 테스트으 성공 여부가 달라질 수 있다. 이를 피하기 위해서 단위 테스트는 고립되어야 한다(의존하지 않아야한다)는 원칙이다.
테스트는 어디서 얼마나 실행하느냐에 관계없이 항상 같은 결과를 만들어내도록 해야한다는 원칙이다.
Mockito 는 단위 테스트를 위한 자바 모킹 프레임워크로 단위 테스트 원칙에 위배되는 의존성을 가짜 객체(Mock Object)로 만들어 의존하지 않도록 하는 역할을 한다.
// Mockito.mock() 메소드를 이용한 방법
Mockito.mock(List::class.java)
// @Mock 어노테이션을 이용한 방법
@Mock
val mockList = List()
Stubbing 은 모킹한 객체의 동작을 정의하는 것이다. 가짜 객체들은 메소드에 대한 구현을 갖지 않는데 이 메소드에 임의의 반환값을 지정하는 것이다.
자세한 사용법은 아래 링크 참조
class RegisterService(
private val memberRepositoty: MemberRepository
){
...
}
MemberRepository
에 의존하는 RegisterService
클래스 테스트 코드는 아래와 같다.
@ExtendWith(MockitoExtension::class)
RegisterServiceTest{
// Mock 객체 생성
@Mock
lateinit var memberRepository: MemberRepository
// 의존성 주입 및 Mock 객체 주입
@Autowired
@InjectMocks
lateinit var registerService: RegisterService
@Test
fun localRegister() {
val dto = LocalRegisterRequestDto(
emailAddress = "test@test.com",
password = "asdfasdfd",
nickname = null,
phoneNumber = "010-1234-1234",
)
val member = Member(
id = 1L,
emailAddress = "test@test.com",
password = "asdfasdfd",
nickname = null,
phoneNumber = "010-1234-1234",
registerType = RegisterType.LOCAL
)
// stubbing
Mockito.`when`(memberRepository.save(Mockito.any(Member::class.java))).thenReturn(member)
assertThat(registerService.localRegister(dto)).isEqualTo(member.id)
}
}
휴대폰 문자 발송을 위한 coolSMS 서비스를 통해 문자를 실제로 날리게 되면 요금이 부과되기 때문에 테스트 시 문자를 날리게 할 수 없어 이 서비스를 모킹하려 하였다.
첫번째 문제는 휴대폰 문자 발송을 위한 coolSMS 서비스가 스프링 컨테이너에 빈으로 올라가 있지 않아 테스트에서 의존성을 스프링 컨테이너가 주입시켜줄수 없는 것이었고
두번째 문제는 Mockito(@Mock)으로 모킹한 휴대폰 문자 발송 서비스를 서비스에 주입할 수 없다는 것이었다.
첫번째 문제는 간단하게 설정 클래스를 만들어 @Bean
어노테이션으로 빈을 등록하는 것이었다. 스프링의 가장 기본적인 내용이지만 컴포넌트 스캔을 통한 자동 빈 등록에 익숙해져있어 생각해내지 못해 시간을 소요했다.
두번째 문제는 @MockBean
어노테이션의 사용으로 해결하였다. 통합 테스트는 스프링 컨테이너가 모든 빈을 로드해 의존성을 주입해주기 때문에 가짜 객체를 주입해줄 수 없었다. @MockBean
어노테이션을 사용하면 해당 빈을 가짜 객체로 바꿔준다. 스프링 컨테이너에 가짜 객체가 빈으로 바꿔치기 되었기 때문에 스프링 컨테이너가 알아서 가짜 객체를 서비스에 주입한다.