본문 바로가기

프로그래밍 기술 노트/기타 정보

속성기반 테스팅 [property based testing]

프로그래머는 모듈 혹은 함수를 만들면 유닛 테스트 (단위 테스트) 를 진행한다.
테스트는 해당 함수가 정상적으로 동작하는지를 테스트 하는것이다.
함수가 정상적으로 동작하는 것을 보장 및 확인하는것은 프로그래머에게 아주 중요하기 때문에 테스트는 아주 중요한 작업이다. 테스트는 참 중요하기 때문에 TDD 라는 테스트를 먼저 만들고 해당 테스트를 통과하는 함수를 제작하는 방법론까지 존재한다.
일반적으로 단위테스트를 하는 방법은 다음과 같다.
1. 인풋과 아웃풋을 계산함.
2. 함수가 해당 인풋을 받을때 함수의 아웃풋이 정상적인지 확인.

예를들어 두 값을 더하는 함수를 만들었다고 해보자.


public static class Calc
{
    public static int Add(int x, int y) { return x + y; }
}
[Test]
public void Test1()
{
    const int input1 = 2; const int input2 = 4; const int output = 6; int answer = Calc.Add(input1, input2); Assert.AreEqual(output, answer);
}

Add 함수는 두수를 더하는 함수이고
Test1 는 2,4를 인풋으로 받았을때 아웃풋이 6이 나오는지 확인하는 테스트이다.

그러나 이렇게 유닛테스트를 하게되면 문제점이있는데
Add 함수를 다음과 같이 수정해도 테스트를 통과 하게 된다.

 public static int Add(int x, int y) { return 6; }


이러한 잘못된 함수가 테스트를 통과하는것을 막기위하여 테스트를 하나더 추가해보자

 [Test]
 public void Test2() 
 { 
 	int answer = Calc.Add(1, 1); Assert.AreEqual(2, answer); 
 }

1과 1을 더하면 2가 나오는지 확인하는 테스트이다.

잘못된 함수는 위의 테스트를 통과하지 못한다.
그러나 함수를 다음과 같이 수정하면 다시 테스트를 통과한다.

 public static int Add(int x, int y) { if (x == 1) { return 2; } return 6; }


물론 개발자가 이렇게 고의로 잘못된 프로그래밍을 하지는 않겠지만
단위 테스트의 본질적인 문제는 Fail case 를 알 수 없다는것이다.

위의 그림같이 invaild data 가 존재하는 함수이지만 Unit Test를 valid data만을 가지고 하게되면 모든 테스트는 통과할것이다.
또한 프로그래머라면 다들 알다시피 테스트를 작성하는것은 유지보수 작업을 하는것만큼이나 매우 귀찮은 작업이기때문에 Unit Test를 대충하는경향이 있다. 따라서 잘못된 함수더라도 모든 테스트를 통과하는 경우가 발생한다.

이러한 문제를 해결하기위하여 나온 테스트 방법론이 속성기반 테스트이다.
간단한 예를 들자면
Add는 두 값의 합이다.
합은 교환 법칙이 성립한다. 따라서 Add 함수또한 교환 법칙이 성립해야한다.
합은 결합 법칙이 성립한다. 따라서 1+1+1 은 1 + (1+1), 즉 1+ 2 와 같다.
0을 더하면 값이 변경되지 않는다.
같은 수를 더하면 2배가 된다.
와 같은 여러가지 속성이 존재한다.
이러한 속성 자체를 검사하는것이 속성기반 테스팅이다. 이러한 속성기반 테스팅을 쉽게 돕는 라이브러리가 QuickCheck 이다.
QuickCheck를 사용하면 함수의 속성(property) 을 정의하고 자동으로 테스트케이스를 생성한다(Generative Testing)
QuickCheck를 Haskell 의 라이브러리를 시작으로 다른 언어에서도 포팅되었으며
C# 및 F#의 경우 fsCheck를 , clojure 에서는 clojure.spec 을 사용한다.

[Property]
public Property DoubleValue(int x) { return (x * 2 == Calc.Add(x, x)).ToProperty(); }
[Property]
public Property Associated(int x, int y) { return (Calc.Add(x, Calc.Add(y, y)) == Calc.Add(x, 2 * y)).ToProperty(); }
[Property]
public Property Commutative(int x, int y) { return (Calc.Add(x, y) == Calc.Add(y, x)).ToProperty(); }
[Property]
public Property AddingZero(int x) { return (Calc.Add(x, 0) == x).ToProperty(); }

위의 Add 예제를 C# 으로 속성기반 테스트 하는 예제이다.
QuickCheck 계열? 을 사용하게 되면 알아서 값을 넣어가면서 테스트도 돌리고 잘못된 결과를 알려준다
사용하는 방법에 따라서 분류, 확인등 다양한 기능도 제공된다.
속성기반 테스트와 유닛테스트를 같이 사용하면 보다 견고한 프로그래밍이 가능할것이다.

관련 내용
https://fsharpforfunandprofit.com/pbt/

 

An introduction to property based testing | F# for fun and profit

This page contains links to the slides and code from my talk “An introduction to property based testing”. We are all familiar with example-based testing, as typified by TDD and BDD. Property-based testing takes a very different approach, where a single tes

fsharpforfunandprofit.com



728x90