데이터 흐름 테스팅 (Data Flow Testing)

데이터 흐름 테스팅은 프로그램에서 변수의 정의와 사용 위치에 초점을 맞춰 테스트 케이스를 설계하고 실행하는 기법이다.
이 방법은 데이터가 프로그램 내에서 어떻게 생성되고 전달되는지를 확인하는 데 중점을 둔다.

데이터 흐름에서 발생할 수 있는 주요 활동들:

  1. 정의(Definition): 변수에 값이 할당되는 지점
  2. 사용(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 변수의 데이터 흐름을 분석해보면:

  1. 초기 정의: discount = 0
  2. 조건에 따른 재정의: quantity에 따라 0.1 또는 0.05로 설정
  3. 계산에서의 사용: 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

주요 특징

  1. 데이터 중심: 변수의 정의와 사용에 초점을 맞춘다.
  2. 제어 흐름 그래프 활용: 데이터 흐름 테스트는 제어 흐름 그래프에 데이터 사용 현황을 추가한 그래프를 통해 테스트를 수행한다.
  3. 화이트박스 테스트: 프로그램의 내부 구조를 이해하고 있어야 수행할 수 있는 화이트박스 테스트 기법이다.

목적

  1. 데이터가 정확히 생성, 전달, 변환되는지 확인
  2. 데이터 손실이나 중복 발생 여부 확인
  3. 데이터가 필요한 모듈에 정확한 값으로 전달되는지 검증

장점

  1. 데이터의 흐름을 추적하여 잠재적인 오류를 발견할 수 있다.
  2. 데이터 관련 문제를 조기에 식별하여 예방할 수 있다.
  3. 복잡한 데이터 처리 로직을 체계적으로 테스트할 수 있다.

단점

  1. 데이터 경로가 복잡한 시스템에서는 모든 흐름을 추적하는 데 비용이 많이 들 수 있다.
  2. 테스트 케이스 설계와 실행에 상당한 시간과 노력이 필요할 수 있다.

적용 단계

데이터 흐름 테스팅은 설계 단계(모델)와 구현 단계(코드) 모두에서 검증이 필요하다.

  1. 모델 단계: 데이터의 전체 흐름을 설계하고 데이터 전달이 의도한 대로 이루어지는지 확인한다.
  2. 코드 단계: 실제 데이터가 모델 설계대로 이동하고, 필요한 데이터가 누락되거나 중복되지 않는지 확인한다.

데이터 흐름 테스팅은 소프트웨어의 데이터 처리 로직을 철저히 검증하여 데이터 관련 오류를 최소화하는 데 중요한 역할을 한다.
특히 데이터 중심적인 애플리케이션이나 복잡한 데이터 처리 로직을 가진 시스템에서 유용하게 활용될 수 있다.

데이터 흐름 테스팅에서 주의해야 할 주요 결함 패턴들

  1. 정의-정의(DD) 이상: 사용되지 않고 재정의되는 경우

    1
    2
    
    total = 0
    total = 100  # 첫 번째 정의가 사용되지 않음
    
  2. 정의-사용(DU) 이상: 정의된 변수가 사용되지 않는 경우

    1
    2
    
    result = calculate_value()  # result가 이후에 사용되지 않음
    return 0
    
  3. 미정의-사용(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)

데이터 흐름 테스팅을 효과적으로 수행하기 위한 전략은 다음과 같다:

  1. 변수의 전체 생명주기 추적
    - 변수가 언제 생성되고, 수정되며, 사용되는지 파악
    - 각 단계에서의 값의 유효성 검증

  2. 경계 조건 고려
    - 변수가 초기화되지 않은 상태
    - 최대/최소값 경계
    - null 또는 undefined 상태

  3. 복잡한 데이터 구조 처리
    - 객체의 속성 변경
    - 컬렉션 요소의 수정
    - 참조 전달과 값 전달의 차이


참고 및 출처