데이터 흐름 테스팅 (Data Flow Testing)#
데이터 흐름 테스팅은 프로그램에서 변수의 정의와 사용 위치에 초점을 맞춰 테스트 케이스를 설계하고 실행하는 기법이다.
이 방법은 데이터가 프로그램 내에서 어떻게 생성되고 전달되는지를 확인하는 데 중점을 둔다.
데이터 흐름에서 발생할 수 있는 주요 활동들:
- 정의(Definition): 변수에 값이 할당되는 지점
- 사용(Use): 변수의 값이 읽히는 지점
- 계산용(Computational use): 다른 값을 계산하는데 사용
- 조건용(Predicate use): 조건문에서 사용
예제 코드를 통한 데이터 흐름:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| def calculate_final_price(base_price, quantity):
# 변수 정의(Definition)
discount = 0
# 조건용 사용(Predicate use)
if quantity > 10:
# 변수 정의(Definition)
discount = 0.1
elif quantity > 5:
discount = 0.05
# 계산용 사용(Computational use)
final_price = base_price * quantity * (1 - discount)
return final_price
|
이 코드에서 discount
변수의 데이터 흐름을 분석해보면:
- 초기 정의: discount = 0
- 조건에 따른 재정의: quantity에 따라 0.1 또는 0.05로 설정
- 계산에서의 사용: final_price 계산에 사용
이러한 흐름을 테스트하기 위한 테스트 케이스를 설계:
1
2
3
4
5
6
7
8
9
| def test_calculate_final_price():
# 할인 없는 경우 테스트 (quantity <= 5)
assert calculate_final_price(100, 3) == 300
# 5% 할인 케이스 테스트 (5 < quantity <= 10)
assert calculate_final_price(100, 7) == 665 # 100 * 7 * 0.95
# 10% 할인 케이스 테스트 (quantity > 10)
assert calculate_final_price(100, 15) == 1350 # 100 * 15 * 0.9
|
주요 특징#
- 데이터 중심: 변수의 정의와 사용에 초점을 맞춘다.
- 제어 흐름 그래프 활용: 데이터 흐름 테스트는 제어 흐름 그래프에 데이터 사용 현황을 추가한 그래프를 통해 테스트를 수행한다.
- 화이트박스 테스트: 프로그램의 내부 구조를 이해하고 있어야 수행할 수 있는 화이트박스 테스트 기법이다.
- 데이터가 정확히 생성, 전달, 변환되는지 확인
- 데이터 손실이나 중복 발생 여부 확인
- 데이터가 필요한 모듈에 정확한 값으로 전달되는지 검증
- 데이터의 흐름을 추적하여 잠재적인 오류를 발견할 수 있다.
- 데이터 관련 문제를 조기에 식별하여 예방할 수 있다.
- 복잡한 데이터 처리 로직을 체계적으로 테스트할 수 있다.
- 데이터 경로가 복잡한 시스템에서는 모든 흐름을 추적하는 데 비용이 많이 들 수 있다.
- 테스트 케이스 설계와 실행에 상당한 시간과 노력이 필요할 수 있다.
적용 단계#
데이터 흐름 테스팅은 설계 단계(모델)와 구현 단계(코드) 모두에서 검증이 필요하다.
- 모델 단계: 데이터의 전체 흐름을 설계하고 데이터 전달이 의도한 대로 이루어지는지 확인한다.
- 코드 단계: 실제 데이터가 모델 설계대로 이동하고, 필요한 데이터가 누락되거나 중복되지 않는지 확인한다.
데이터 흐름 테스팅은 소프트웨어의 데이터 처리 로직을 철저히 검증하여 데이터 관련 오류를 최소화하는 데 중요한 역할을 한다.
특히 데이터 중심적인 애플리케이션이나 복잡한 데이터 처리 로직을 가진 시스템에서 유용하게 활용될 수 있다.
데이터 흐름 테스팅에서 주의해야 할 주요 결함 패턴들#
정의-정의(DD) 이상: 사용되지 않고 재정의되는 경우
1
2
| total = 0
total = 100 # 첫 번째 정의가 사용되지 않음
|
정의-사용(DU) 이상: 정의된 변수가 사용되지 않는 경우
1
2
| result = calculate_value() # result가 이후에 사용되지 않음
return 0
|
미정의-사용(UR) 이상: 정의되지 않은 변수를 사용하는 경우
1
2
| if total > limit: # limit 변수가 정의되지 않음
return True
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| class BankAccount:
def __init__(self, initial_balance):
self.balance = initial_balance
def transfer(self, amount, target_account):
if self.balance >= amount:
# 데이터 흐름: balance 정의 -> 사용 -> 재정의
self.balance -= amount
target_account.balance += amount
return True
return False
def apply_interest(self, rate):
# 데이터 흐름: balance 사용 -> 재정의
interest = self.balance * rate
self.balance += interest
|
이 코드의 데이터 흐름을 테스트하기 위한 테스트 시나리오는 다음과 같다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| def test_bank_account_data_flow():
# 초기 balance 정의 테스트
account1 = BankAccount(1000)
account2 = BankAccount(500)
# transfer 메서드에서의 데이터 흐름 테스트
assert account1.transfer(300, account2) == True
assert account1.balance == 700
assert account2.balance == 800
# 불충분한 잔액 케이스 테스트
assert account1.transfer(1000, account2) == False
# apply_interest 메서드에서의 데이터 흐름 테스트
account1.apply_interest(0.1)
assert account1.balance == 770 # 700 + (700 * 0.1)
|
데이터 흐름 테스팅을 효과적으로 수행하기 위한 전략은 다음과 같다:
변수의 전체 생명주기 추적
- 변수가 언제 생성되고, 수정되며, 사용되는지 파악
- 각 단계에서의 값의 유효성 검증
경계 조건 고려
- 변수가 초기화되지 않은 상태
- 최대/최소값 경계
- null 또는 undefined 상태
복잡한 데이터 구조 처리
- 객체의 속성 변경
- 컬렉션 요소의 수정
- 참조 전달과 값 전달의 차이
참고 및 출처#