Java Web Project Unit Test #
프로젝트를 진행하다 보면 단위 테스트를 수행해야 하는 경우가 있습니다. 이 글을 통해 java 언어로 개발 시 단위 테스트를 효율적으로 진행하기 위한 내용을 공유하고자 합니다.
단위 테스트의 목적 #
단위 테스트를 진행할 경우 데이터베이스에 접근하는 것은 일반적으로 권장되지 않습니다. 단위 테스트의 목적은 소프트웨어의 개별적인 구성 요소를 독립적으로 검증하는 것이기 때문에, 외부 시스템에 의존하지 않고 테스트를 수행해야 합니다. 그러나, 특정 경우에 데이터베이스와의 상호작용을 포함하는 로직을 테스트해야 할 필요가 있을 수 있습니다. 여기서는 주로 목 객체(Mock Object)를 사용한 테스트 방법에 대해 설명하겠습니다.
데이터베이스에 접근을 권장하지 않는 이유 #
- 속도 문제: 단위 테스트의 핵심 목적 중 하나는 빠른 피드백을 제공하는 것입니다. 데이터베이스에 접근하는 테스트는 네트워크 지연, 데이터베이스 초기화, 데이터 조회 및 삽입 등으로 인해 실행 시간이 길어집니다. 이는 개발자가 지속적으로 코드를 반복하고 빠르게 피드백을 받는 과정을 방해할 수 있습니다.
- 환경 의존성: 단위 테스트는 가능한 한 독립적으로 실행될 수 있어야 합니다. 데이터베이스에 접근하게 되면 테스트 환경을 구성하기 위한 추가 작업이 필요하며, 이는 테스트 실행에 대한 외부 의존성을 증가시킵니다. 이러한 의존성은 테스트의 신뢰성을 저하시키고, 다양한 환경에서의 일관된 테스트 실행을 어렵게 만듭니다.
- 복잡성 증가: 데이터베이스와의 상호작용을 포함하는 단위 테스트는 테스트의 복잡성을 증가시킵니다. 데이터베이스 상태 관리, 연결 관리, 데이터 정리 등 추가적인 관리 작업이 필요하며, 이는 테스트 코드를 유지보수하기 어렵게 만듭니다.
- 격리 문제: 단위 테스트는 테스트되는 코드 단위 외에는 어떤 외부 상태에도 의존해서는 안 됩니다. 데이터베이스를 사용하면 테스트 간 데이터 상태가 공유될 위험이 있으며, 이는 테스트 간의 격리를 보장하기 어렵게 만듭니다. 이로 인해 한 테스트의 실패가 다른 테스트에 영향을 줄 수 있습니다.
- 실제 환경과의 차이: 개발 환경의 데이터베이스와 실제 운영 환경의 데이터베이스 간에는 설정, 데이터, 버전 차이가 있을 수 있습니다. 이러한 차이로 인해 개발 중에는 문제가 없어 보이나 실제 환경에 배포했을 때 예기치 않은 문제가 발생할 수 있습니다.
목 객체(Mock Object) 사용하기 #
목 객체는 실제 객체를 흉내 내는 가짜 객체로, 테스트 대상이 되는 코드와 상호작용합니다. 이 방법을 사용하면 실제 데이터베이스에 의존하지 않고도 데이터베이스 호출을 시뮬레이션 할 수 있습니다.
Mockito 라이브러리 사용 #
Maven 의존성 추가 #
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.x</version>
<scope>test</scope>
</dependency>
Gradle 의존성 추가 #
dependencies {
testImplementation 'org.mockito:mockito-core:3.+'
}
버전문제가 있을 경우 https://mvnrepository.com 로 접속하여 현재 개발에 사용되는 버전과 호환이 되는 버전을 찾아서 사용하세요.
Test 작성 #
서비스 인터페이스 목 객체 생성 #
데이터베이스를 사용하는 서비스 인터페이스에 대한 목 객체를 생성합니다.
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@TestInstance(Lifecycle.PER_CLASS)
public class DemoServiceTest {
@Mock
private DemoService demoService;
@BeforeAll
public void init() {
MockitoAnnotations.openMocks(this);
}
}
테스트 케이스 작성 #
목 객체를 사용하여 테스트 케이스를 작성합니다. 특정 메소드 호출이 예상하는 값을 반환하도록 설정할 수 있습니다.
import static org.mockito.Mockito.*;
@Test
public void testMyServiceMethod() {
// 목 객체에 대한 행동 설정
when(demoService.getData()).thenReturn("Hello");
// 테스트 대상 메소드 실행
String result = demoService.getData();
// 결과 검증
assertThat("Hello").isEqualTo(result);
}
결론 #
단위 테스트에 데이터베이스 연결이 권장되지 않는 것과 Mock을 사용하면 된다는 것은 알고 있었으나, 이번에 이유를 찾아보면서 ‘‘왜 그래야 하는가’‘에 대해 알게 되면서 단위 테스트는 꼭 데이터베이스 연결 없이 테스트를 해야겠다고 마음 먹었습니다.
저도 계속 배워나가는 입장이라 틀린 부분이 있을 수 있으나, 이 글을 통해 개발을 처음 접해보시는 분들에게 조금이나마 도움이 되길 바랍니다.