[스프링부트] 통합 테스트에서의 모킹

tkppp·2022년 3월 2일
1

스프링부트로 회원가입에 필요한 휴대폰 인증 서비스에 대한 테스트를 작성하면서 알게된 점을 기록한 포스트입니다.

F.I.R.S.T 원칙 - 단일 테스트

Fast

단위 테스트는 빨라야 한다는 원칙이다. 스프링부트에서 테스트를 위해 @SpringBootTest 어노테이션을 사용하면 스프링 컨테이너에 등록된 모든 빈이 로드된다. 이는 테스트의 무거움을 불러오고 테스트의 속도를 저하시킨다.

Fast 원칙에 위배되기 때문에 스프링부트에서 @SpringBootTest 어노테이션 자체가 통합테스트를 위한 어노테이션이긴 하지만 @SpringBootTest 어노테이션을 통한 테스트는 단위 테스트라 할 수 없다.

Isolated

테스트가 다른 테스트 혹은 외부 소스등 통제할 수 없는 외부 상황에 의존할 경우 테스트으 성공 여부가 달라질 수 있다. 이를 피하기 위해서 단위 테스트는 고립되어야 한다(의존하지 않아야한다)는 원칙이다.

Repeatable

테스트는 어디서 얼마나 실행하느냐에 관계없이 항상 같은 결과를 만들어내도록 해야한다는 원칙이다.

Mockito를 이용한 모킹

Mockito 는 단위 테스트를 위한 자바 모킹 프레임워크로 단위 테스트 원칙에 위배되는 의존성을 가짜 객체(Mock Object)로 만들어 의존하지 않도록 하는 역할을 한다.

Mock 객체 만들기

// Mockito.mock() 메소드를 이용한 방법
Mockito.mock(List::class.java)

// @Mock 어노테이션을 이용한 방법
@Mock
val mockList = List()

Stubbing

Stubbing 은 모킹한 객체의 동작을 정의하는 것이다. 가짜 객체들은 메소드에 대한 구현을 갖지 않는데 이 메소드에 임의의 반환값을 지정하는 것이다.

자세한 사용법은 아래 링크 참조

Mock 객체 주입

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 어노테이션을 사용하면 해당 빈을 가짜 객체로 바꿔준다. 스프링 컨테이너에 가짜 객체가 빈으로 바꿔치기 되었기 때문에 스프링 컨테이너가 알아서 가짜 객체를 서비스에 주입한다.

0개의 댓글