학습
Scala 고급 테스트 기법
Scala 고급 테스트 기법 — 고급
고급 테스트 기법
1. 도입 (왜 배워야 하는가)
소프트웨어 개발에서 테스트는 필수적인 과정입니다. 특히, 고급 테스트 기법은 복잡한 시스템을 다루는 데 필요합니다. 실무에서는 코드의 품질을 보장하고 버그를 조기에 발견하기 위해 자동화된 테스트를 작성합니다. 고급 테스트 기법을 배우지 않으면 다음과 같은 문제가 발생할 수 있습니다:
- 버그 발견 지연: 초기 단계에서 버그를 발견하지 못하면, 나중에 수정 비용이 증가합니다.
- 기능 추가 시 리그레션 발생: 기존 기능을 수정하거나 새로운 기능을 추가할 때 기존 기능에 문제가 발생할 수 있습니다.
- 코드 품질 저하: 테스트가 없으면 코드의 품질이 저하되고, 유지 보수가 어려워집니다.
- Mocking: 친구와의 약속을 대신 잡아주는 대리인처럼, 실제 객체 대신 가짜 객체를 사용하여 특정 동작을 시뮬레이션합니다.
- Stubbing: 요리 레시피에서 재료를 고정해 두듯이, 특정 메서드 호출에 대해 항상 같은 결과를 반환하도록 설정합니다.
- Property-Based Testing: 다양한 재료로 요리해 보는 것처럼, 다양한 입력을 사용하여 코드의 동작을 검증합니다.
2. 핵심 개념 설명
고급 테스트 기법에는 여러 가지가 있지만, 여기서는 특히 중요한 세 가지 개념을 설명하겠습니다.
| 개념 | 정의 |
|---|---|
| Mocking | 테스트 중에 의존성을 대체하여 특정 동작이나 상태를 시뮬레이션하는 기법 |
| Stubbing | 특정 메서드 호출에 대한 고정된 응답을 제공하는 기법 |
| Property-Based Testing | 다양한 입력을 자동으로 생성하여 테스트하는 기법 |
실생활 비유
3. 기본 코드 예제
여기서는 ScalaTest와 Mockito를 사용한 Mocking과 Stubbing의 기본 예제를 보여드리겠습니다.
import org.scalatest.flatspec.AnyFlatSpec
import org.mockito.Mockito._
class Calculator {
def add(a: Int, b: Int): Int = a + b
}
class CalculatorTest extends AnyFlatSpec {
"Calculator" should "add two numbers" in {
val calculator = new Calculator
assert(calculator.add(2, 3) 5)
}
it should "mock add method" in {
val mockCalculator = mock(classOf[Calculator])
when(mockCalculator.add(2, 3)).thenReturn(10)
// mockCalculator의 add 메서드를 호출하면 항상 10을 반환
assert(mockCalculator.add(2, 3) 10)
}
}
실행 결과
- Calculator should add two numbers
- Calculator should mock add method
코드 한 줄씩 동작 설명
import org.scalatest.flatspec.AnyFlatSpec: ScalaTest의 FlatSpec 스타일을 가져옵니다.class Calculator: Calculator 클래스를 정의합니다.def add(a: Int, b: Int): Int = a + b: 두 숫자를 더하는 메서드를 정의합니다.class CalculatorTest extends AnyFlatSpec: 테스트 클래스를 정의합니다.it should "add two numbers" in {...}: 첫 번째 테스트 케이스를 정의합니다.assert(calculator.add(2, 3) == 5): 2와 3을 더한 결과가 5인지 확인합니다.it should "mock add method" in {...}: 두 번째 테스트 케이스를 정의합니다.val mockCalculator = mock(classOf[Calculator]): Calculator 클래스의 mock 객체를 생성합니다.when(mockCalculator.add(2, 3)).thenReturn(10): mock 객체의 add 메서드가 항상 10을 반환하도록 설정합니다.assert(mockCalculator.add(2, 3) == 10): mock 객체의 add 메서드를 호출하고 결과를 검증합니다.- 잘못된 코드:
- 올바른 코드:
- 잘못된 코드:
- 올바른 코드:
- Mocking과 Stubbing은 테스트에서 의존성을 관리하는 중요한 기법입니다.
- Property-Based Testing은 다양한 입력을 사용하여 코드의 정확성을 검증합니다.
- 테스트를 통해 버그를 조기에 발견하고, 코드 품질을 향상시킬 수 있습니다.
- Test-Driven Development (TDD)
- Behavior-Driven Development (BDD)
- Continuous Integration (CI)와 테스트 자동화
4. 실전 활용 예제
예제 1: 쇼핑몰 장바구니의 총합 계산
쇼핑몰에서 장바구니의 총합을 계산할 때, 각 아이템의 가격을 가져오는 메서드를 Mocking하여 테스트할 수 있습니다.
class ShoppingCart {
def totalPrice(items: List[Int]): Int = items.map(price).sum
def price(itemId: Int): Int = {
// 실제 DB에서 가격을 가져오는 로직
???
}
}
// 테스트 코드
class ShoppingCartTest extends AnyFlatSpec {
it should "calculate total price using mocked price method" in {
val mockCart = mock(classOf[ShoppingCart])
when(mockCart.price(1)).thenReturn(100)
when(mockCart.price(2)).thenReturn(200)
val total = mockCart.totalPrice(List(1, 2))
assert(total == 300) // 100 + 200
}
}
예제 2: 사용자 인증 서비스
사용자 인증 서비스에서 외부 API를 호출하는 부분을 Stubbing하여 테스트할 수 있습니다.
class AuthService {
def isAuthenticated(userId: String): Boolean = {
// 외부 API 호출
???
}
}
// 테스트 코드
class AuthServiceTest extends AnyFlatSpec {
it should "return true for authenticated user" in {
val mockAuthService = mock(classOf[AuthService])
when(mockAuthService.isAuthenticated("user123")).thenReturn(true)
assert(mockAuthService.isAuthenticated("user123"))
}
}
5. 흔한 실수와 주의사항
실수 1: Mock 객체를 잘못 사용
val mockCalculator = new Calculator // 실제 객체를 생성
val mockCalculator = mock(classOf[Calculator]) // Mock 객체 생성
실수 2: Stubbing을 잊음
assert(mockCalculator.add(2, 3) == 10) // Stubbing이 없어서 실패
when(mockCalculator.add(2, 3)).thenReturn(10) // Stubbing 추가