단위 테스트에 대한 내용은 여기로 → 2025.09.27 - [Development/개발 공부] - [Test] 단위 테스트(Unit Test)
Stub은 Test Double(테스트 대역) 중 하나로 미리 지정한 값을 반환하여 테스트 대상의 동작을 검증한다.
Stub 기반의 단위 테스트를 진행할 때, 여러 테스트 프레임워크나 라이브러리를 함께 사용할 수 있다.
그 중 JUnit 프레임워크에 대해 자세히 알아보려 한다.
JUnit 프레임워크
JUnit은 단위 테스트 프레임워크로, 작은 단위(메소드, 클래스)를 독립적으로 검증할 수 있도록 지원한다.
검증 결과로 구현 목표에 맞게 잘 동작하는지 확인할 수 있다.
Given- When -Then 패턴
테스트를 진행하는 대표적인 방식인 Given-When-Then 패턴에 대해 알아보겠다.
- Given: 테스트를 진행하기 전, 설정되어야 할 조건을 미리 정의하는 부분
- When: 테스트할 동작을 실행하는 부분
- Then: 테스트 결과를 예상 결과와 대조하는 부분
단위 테스트 구현(예제)
세 단계를 Stub으로 구현하기 위해서는 먼저 테스트 코드를 생성해야 한다.
src 하위에는 main 디텍터리도 있고, test 디렉터리도 있다.

테스트 파일은 별도의 소스 폴더(/test)에 작성하고, 패키지는 원본 클래스와 동일하게 이름을 맞춰주면 된다.
만약 main/java/com.abc.project/ss/ 에 A 클래스 파일이 있다면,
테스트 파일은 test/java/com.abc.project/ss/ 에 ATest 파일을 생성하도록 하자.
(파일 이름은 자유이다!)
먼저 A 클래스 파일을 살펴보자.
class A {
private DataService dataService;
public A(DataService dataService) {
super();
this.dataService = dataService;
}
public int findMaxFromAllData() {
int[] data = dataService.retriveAllData();
int maxValue = Integer.MIN_VALUE;
for (int value : data) {
if (value > maxValue) {
maxValue = value;
}
}
return maxValue;
}
}
A 클래스는 배열 중 가장 큰 값을 return 하는 findMaxFromAllData() 메소드를 가지고 있다.
이제 findMaxFromAllData()를 테스트하기 위한 ATest 파일을 생성하자.
class ATest {
}
class Stub1 implements DataService {
@Override
public int[] retrieveAllData() {
return new int[] {25, 15, 5};
}
}
Test 파일에서 필요한 것은 테스트하기 위한 가짜 객체를 만드는 것이다.
실제 값을 가져오는 대신, 임의로 데이터를 생성해서 가져올 수 있도록 Stub1을 만들어주었다.
class ATest {
// given
Stub1 stub1 = new Stub1();
private A a = new A();
@Test
void findMaxFromAllDataScenario() {
// when
int result = a.findMaxFromAllData(stub1);
// then
assertEquals(25, result);
}
}
class Stub1 implements DataService {
@Override
public int[] retrieveAllData() {
return new int[] {25, 15, 5};
}
}
ATest가 실행되는 흐름은 다음과 같다.
- 가짜 객체를 활용하기 위한 인스턴스, 실제 클래스의 인스턴스를 할당한다.
- 테스트하고자 하는 메소드를 선언하고, @Test 어노테이션으로 단위 테스트임을 표시한다.
- 테스트할 동작을 실행해 결과(result)를 저장하고,
- 예상 결과(25)와 실제 결과가 일치(assertEquals)한지 확인한다.
테스트 결과는 다음과 같다.

Assert 메소드
assert는 예상 결괏값과 실제 실행값을 대조하여 일치 여부를 확인해주는 메소드이다.
예제에서 알아보았듯, assertEquals()는 예상값과 실제값이 같은지 검증하는 메소드이다.
주로 assertEquals(expectedValue, actualValue)로 사용하지만, 검증에 실패할 경우 메시지를 추가할 수 있다.
assertEquals(expectedValue, actualValue, message)
이외에도 여러 Assert 메소드가 있다.
- assertTrue()/assertFalse(): boolean 값이 참/거짓인지 확인
- assertNull()/assertNotNull(): 객체가 null인지 아닌지 확인
- assertArrayEquals(): 배열의 크기와 요소가 같은지 확인
위 메소드 모두 테스트를 실패할 시, 메시지를 제공한다.
또한 AssertJ라는 라이브러리를 통해, 위 함수 이외에도 다양한 검증 메소드를 제공한다.
JUnit 어노테이션
테스트 메소드를 선언할 때의 @Test 어노테이션 말고도 다양한 어노테이션을 활용할 수 있다.
@BeforeEach, @AfterEach
@BeforeEach는 각 테스트 메소드 실행 전에 수행되는 코드임을 나타내는 어노테이션이다.
@AfterEach는 각 테스트 메소드 실행 후에 수행되는 코드임을 나타내는 어노테이션이다.
@BeforeEach
void beforeEach() {
System.out.println("BeforeEach");
}
@AfterEach
void afterEach() {
System.out.println("AfterEach");
}
@Test
void test1() { System.out.println("test1"); }
@Test
void test2() { System.out.println("test2"); }
다음과 같이 테스트 코드가 존재한다면, 출력 순서는
BeforeEach
test1
AfterEach
BeforeEach
test2
AfterEach
가 된다.
@BeforeAll, @AfterAll
@BeforeAll은 모든 테스트 실행 전에 한 번만 수행되는 어노테이션이다.
@AfterAll은 모든 테스트 실행 후에 한 번만 수행되는 어노테이션이다.
이때 주의할 점은 해당 어노테이션을 가지고 있는 unit은 static이어야 한다.
클래스 레벨에서 한 번만 실행되기 때문에, 전체 테스트를 공통 설정하기에 좋은 어노테이션!
사용 예시는 다음과 같다.
@BeforeAll
static void beforeAll() {
System.out.println("BeforeAll");
}
@AfterAll
static void afterAll() {
System.out.println("AfterAll");
}
@Test
void test1() { System.out.println("test1"); }
@Test
void test2() { System.out.println("test2"); }
출력 순서는
BeforeAll
test1
test2
AfterAll
이 된다.
마무리
JUnit과 Stub을 이용해 단위 테스트를 알아보았다.
Stub은 특정 케이스를 검증하기는 쉽지만, 여러 시나리오를 추가하려면 Stub 클래스를 계속해서 만들어야 한다.
또한 인터페이스가 바뀌면 Stub 클래스도 일일히 바꿔주어야 한다.
❓이러한 점을 보완할 수 있는 테스트 방법도 존재할까요? 🧐
❗️(네 그렇습니다..)
다음 시간엔 Mockito에 대해서 알아보겠다!!
👏
참고
'Development > 개발 공부' 카테고리의 다른 글
| [NoSQL] Redis란? (0) | 2025.10.17 |
|---|---|
| [Test] Mock을 이용한 단위 테스트(with Mockito) (0) | 2025.10.01 |
| [Test] 단위 테스트(Unit Test) (0) | 2025.09.27 |
| WEB / WAS(Web Application Server) (0) | 2025.09.17 |
| 가상 머신(VM, Virtual Machine) (0) | 2025.08.27 |