단위 테스트란?
단위 테스트(Unit Testing)는 소프트웨어 개발 과정에서 개별 코드 단위(함수, 메소드, 클래스 등)가 예상대로 작동하는지 검증하는 절차를 말합니다.
단위 테스트를 통해 각 모듈의 기능을 독립적으로 검사함으로써, 프로그램 전체의 신뢰성과 안정성을 높일 수 있습니다.
또한, 리팩토링이나 유지보수 과정에서 발생할 수 있는 오류를 사전에 방지하는 데에도 큰 도움이 됩니다.
JUnit이란?
JUnit은 Java 언어를 위한 대표적인 단위 테스팅 프레임워크입니다.
간단한 어노테이션과 assertion 메소드를 사용하여 테스트 코드를 작성할 수 있으며, 테스트 결과를 자동으로 검증해줍니다.
JUnit은 이클립스, 인텔리제이 등 주요 IDE에서 기본적으로 지원하고 있어 손쉽게 적용할 수 있습니다. 현재 최신 버전은 JUnit 5이며, 이전 버전인 JUnit 4도 여전히 많이 사용되고 있습니다.
JUnit 테스트 작성 예제
그럼 이제 실제로 JUnit을 이용하여 단위 테스트를 작성하는 방법을 예제와 함께 살펴보겠습니다.
예제 1: 문자열 덧셈 계산기 먼저, 간단한 문자열 덧셈 계산기를 구현하고, 이에 대한 단위 테스트를 작성해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class StringCalculator {
public static int add(String numbers) {
if (numbers == null || numbers.isEmpty()) {
return 0;
}
String[] nums = numbers.split(“[,:]”);
int sum = 0;
for (String num : nums) {
sum += Integer.parseInt(num.trim());
}
return sum;
}
}
|
cs |
위 클래스는 컴마(,)나 콜론(:)으로 구분된 숫자 문자열을 입력받아, 각 숫자의 합을 반환하는 add 메소드를 가지고 있습니다.
이제 이 메소드가 제대로 동작하는지 검증하는 테스트 코드를 작성해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class StringCalculatorTest {
@Test
public void testEmptyString() {
assertEquals(0, StringCalculator.add(“”));
}
@Test
public void testSingleNumber() {
assertEquals(3, StringCalculator.add(“3”));
}
@Test
public void testTwoNumbers() {
assertEquals(7, StringCalculator.add(“3,4”));
}
@Test
public void testMultipleNumbers() {
assertEquals(10, StringCalculator.add(“1,2,3,4”));
}
@Test
public void testNewLineSeparator() {
assertEquals(6, StringCalculator.add(“1:2:3”));
}
}
|
cs |
위 테스트 클래스는 add 메소드의 다양한 입력 케이스에 대해 기대값과 실제 결과값을 비교하는 테스트 메소드들을 포함하고 있습니다.
@Test 어노테이션을 통해 해당 메소드가 테스트 대상임을 명시하였고, Assertions.assertEquals 메소드로 두 값의 일치 여부를 검사하였습니다.
이처럼 JUnit을 사용하면 각 메소드의 동작을 독립적으로 검증할 수 있어, 개별 기능 단위의 오류를 빠르게 발견하고 수정할 수 있습니다.
예제 2: 할일 관리 애플리케이션 조금 더 복잡한 예제로, 할일 관리 애플리케이션의 핵심 로직에 대한 단위 테스트를 작성해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import java.util.ArrayList;
import java.util.List;
public class TodoService {
private List<String> todos = new ArrayList<>();
public void addTodo(String todo) {
todos.add(todo);
}
public void removeTodo(String todo) {
todos.remove(todo);
}
public List<String> getTodos() {
return todos;
}
public int getTodoCount() {
return todos.size();
}
}
|
cs |
위 클래스는 할일을 추가, 삭제, 조회하는 기능을 가진 간단한 TodoService를 구현한 것입니다.
이제 각 메소드에 대한 테스트 코드를 작성해볼까요?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class TodoServiceTest {
private TodoService todoService;
@BeforeEach
public void setUp() {
todoService = new TodoService();
}
@Test
public void testAddTodo() {
todoService.addTodo(“Buy milk”);
assertEquals(1, todoService.getTodoCount());
}
@Test
public void testRemoveTodo() {
todoService.addTodo(“Buy milk”);
todoService.removeTodo(“Buy milk”);
assertEquals(0, todoService.getTodoCount());
}
@Test
public void testGetTodos() {
todoService.addTodo(“Buy milk”);
todoService.addTodo(“Walk the dog”);
List<String> expectedTodos = Arrays.asList(“Buy milk”, “Walk the dog”);
assertEquals(expectedTodos, todoService.getTodos());
}
}
|
cs |
@BeforeEach 어노테이션을 사용하여 각 테스트 메소드 실행 전에 TodoService 객체를 초기화하였습니다.
이를 통해 테스트 케이스 간의 독립성을 보장할 수 있습니다. 그리고 addTodo, removeTodo, getTodos 메소드에 대한 테스트를 수행하였습니다.
getTodos의 경우 실제 반환값과 기대값(expectedTodos)를 assertEquals로 비교하였습니다.
예제 3: 회원 관리 시스템 마지막으로, 회원 관리 시스템의 일부 기능에 대한 단위 테스트를 작성해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import java.util.HashMap;
import java.util.Map;
public class MemberService {
private Map<String, Member> members = new HashMap<>();
public void join(Member member) {
members.put(member.getEmail(), member);
}
public Member findByEmail(String email) {
return members.get(email);
}
public boolean isEmailExists(String email) {
return members.containsKey(email);
}
}
class Member {
private String name;
private String email;
public Member(String name, String email) {
this.name = name;
this.email = email;
}
// getters and setters
}
|
cs |
MemberService 클래스는 회원 가입, 이메일로 회원 조회, 이메일 중복 체크 기능을 제공합니다.
이에 대한 테스트 코드는 다음과 같이 작성할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class MemberServiceTest {
private MemberService memberService;
@BeforeEach
public void setUp() {
memberService = new MemberService();
}
@Test
public void testJoin() {
Member member = new Member(“John”, “john@example.com”);
memberService.join(member);
assertTrue(memberService.isEmailExists(“john@example.com”));
}
@Test
public void testFindByEmail() {
Member member = new Member(“John”, “john@example.com”);
memberService.join(member);
assertEquals(member, memberService.findByEmail(“john@example.com”));
}
@Test
public void testIsEmailExists() {
Member member = new Member(“John”, “john@example.com”);
memberService.join(member);
assertTrue(memberService.isEmailExists(“john@example.com”));
assertFalse(memberService.isEmailExists(“jane@example.com”));
}
}
|
cs |
join 메소드를 테스트하기 위해 새로운 Member 객체를 생성하여 MemberService에 저장하고, isEmailExists로 해당 이메일의 존재 여부를 검사하였습니다.
findByEmail 메소드는 저장된 Member 객체와 실제로 조회된 객체가 일치하는지 확인하였습니다.
그리고 isEmailExists 메소드는 존재하는 이메일과 존재하지 않는 이메일에 대해 각각 true와 false를 반환하는지 테스트하였습니다.
이상으로 JUnit을 활용한 단위 테스트의 기본적인 내용과 예제를 살펴보았습니다.
단위 테스트는 개발 과정에서 발생할 수 있는 오류를 사전에 발견하고, 코드의 품질을 높이는 데 큰 도움이 됩니다.
더 자세한 내용은 아래 링크를 참고해주세요.
- JUnit 5 User Guide: https://junit.org/junit5/docs/current/user-guide/
- Javadoc for JUnit 5: https://junit.org/junit5/docs/current/api/
- Baeldung JUnit 5 Tutorial: https://www.baeldung.com/junit-5