유닛 테스트란?
유닛 테스트(Unit Test), 또는 단위 테스트는 소프트웨어 개발 과정에서 가장 기본적이고 중요한 테스트 기법 중 하나다. 유닛 테스트의 목적은 애플리케이션의 개별 구성 요소, 즉 "유닛"이 예상대로 작동하는지 확인하는 것이다. 일반적으로 유닛은 메소드, 클래스, 또는 모듈과 같은 작은 코드 단위를 의미한다.
왜 유닛 테스트를 해야 하는가?
유닛 테스트는 여러 가지 이유로 중요하다:
- 코드 품질 향상: 유닛 테스트를 작성하면 코드의 결함을 조기에 발견할 수 있어 전반적인 코드 품질을 높일 수 있다.
- 디버깅 용이: 특정 유닛이 잘못된 동작을 할 경우, 해당 유닛을 빠르게 디버깅할 수 있다.
- 안정성: 코드 변경이 기존 기능에 미치는 영향을 최소화할 수 있다.
- 문서화: 테스트 코드는 코드베이스의 동작을 문서화하는 역할도 한다.
유닛 테스트의 기본 원칙
- 독립성: 각 유닛 테스트는 독립적으로 실행되어야 하며, 다른 테스트에 영향을 주거나 받지 않아야 한다.
- 반복 가능성: 테스트는 언제나 동일한 결과를 반환해야 한다.
- 단순성: 테스트 코드는 이해하기 쉽고 간결해야 한다.
유닛 테스트 작성 방법
유닛 테스트를 작성하는 방법에는 여러 가지가 있다. 여기서는 Java와 JUnit 프레임워크를 사용한 예시를 소개한다.
JUnit을 사용한 유닛 테스트
예제 클래스
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
}
예제 유닛 테스트 클래스
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CalculatorTest {
@Test
public void testAdd() {
Calculator calculator = new Calculator();
int result = calculator.add(2, 3);
assertEquals(5, result);
}
@Test
public void testSubtract() {
Calculator calculator = new Calculator();
int result = calculator.subtract(5, 2);
assertEquals(3, result);
}
}
테스트 코드 설명
- @Test: 해당 메소드가 테스트 메소드임을 JUnit에 알린다.
- assertEquals(expected, actual): 기대값(expected)과 실제값(actual)이 일치하는지 확인한다.
모의 객체(Mock Object) 활용
복잡한 클래스나 외부 시스템과의 의존성을 가진 유닛을 테스트할 때는 모의 객체(Mock Object)를 활용할 수 있다. Mockito와 같은 프레임워크를 사용하면 쉽게 모의 객체를 생성하고 사용할 수 있다.
Mockito를 사용한 예제
예제 서비스 클래스
public class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
예제 유닛 테스트 클래스
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
public void testGetUserById() {
MockitoAnnotations.openMocks(this);
User user = new User();
user.setId(1L);
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
User result = userService.getUserById(1L);
assertEquals(user, result);
}
}
테스트 코드 설명
- @Mock: 모의 객체를 생성한다.
- @InjectMocks: 모의 객체를 주입하여 테스트할 객체를 생성한다.
- when().thenReturn(): 모의 객체의 동작을 정의한다.
유닛 테스트의 한계
유닛 테스트는 유용하지만, 몇 가지 한계도 있다:
- 비즈니스 로직만 테스트: 유닛 테스트는 개별 유닛의 로직을 검증하는 데 중점을 두므로, 통합 테스트만큼 전체 시스템의 동작을 확인하지는 못한다.
- 테스트 범위 제한: 외부 시스템과의 상호작용이나 UI 테스트는 유닛 테스트로 다루기 어렵다.
마무리
유닛 테스트는 소프트웨어 개발에서 필수적인 부분이다. 유닛 테스트를 통해 코드 품질을 높이고, 디버깅을 용이하게 하며, 안정적인 애플리케이션을 유지할 수 있다. JUnit과 Mockito와 같은 도구를 활용하면 유닛 테스트를 더 효과적으로 작성할 수 있다. 개발의 초기 단계에서부터 유닛 테스트를 잘 활용하면 나중에 발생할 수 있는 문제를 미연에 방지할 수 있을 것 같다.
'개념 정리 > 기타 개념' 카테고리의 다른 글
Hexagonal Architecture란? (3) | 2024.07.23 |
---|