1. 개요

이 기사에서는 Property Testing 의 개념과 vavr-test 라이브러리 에서의 구현에 대해 살펴보겠습니다 .

속성 기반 테스트 (PBT)를 사용하면 프로그램 이 준수해야 하는 불변성에 관한 상위 수준 동작을 지정할 수 있습니다.

2. 속성 테스트란 무엇입니까?

속성은 불변과 입력 값 생성기 의 조합입니다 . 생성된 각 값에 대해 불변은 조건자로 처리되고 해당 값에 대해 true 또는 false를 생성하는지 여부를 확인합니다.

false를 생성하는 값이 하나 있는 즉시 속성이 위조되었다고 하며 검사가 중단됩니다. 특정 양의 샘플 데이터 후에 속성을 무효화할 수 없으면 속성이 충족된 것으로 간주됩니다.

이러한 동작 덕분에 불필요한 작업을 수행하지 않고 조건이 충족되지 않으면 테스트가 빠르게 실패합니다.

3. 메이븐 의존성

먼저 vavr-test 라이브러리에 Maven 의존성을 추가해야 합니다.

<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>jvavr-test</artifactId>
    <version>${vavr.test.version}</version>
</dependency>

<properties>
    <vavr.test.version>2.0.5</vavr.test.version> 
</properties>

4. 속성 기반 테스트 작성

문자열 스트림을 반환하는 함수를 생각해 봅시다. 간단한 규칙에 따라 숫자를 문자열에 매핑하는 0 위쪽의 무한 스트림입니다. 우리는 여기에서 Pattern Matching 이라는 흥미로운 Vavr 기능을 사용하고 있습니다 .

private static Predicate<Integer> divisibleByTwo = i -> i % 2 == 0;
private static Predicate<Integer> divisibleByFive = i -> i % 5 == 0;

private Stream<String> stringsSupplier() {
    return Stream.from(0).map(i -> Match(i).of(
      Case($(divisibleByFive.and(divisibleByTwo)), "DividedByTwoAndFiveWithoutRemainder"),
      Case($(divisibleByFive), "DividedByFiveWithoutRemainder"),
      Case($(divisibleByTwo), "DividedByTwoWithoutRemainder"),
      Case($(), "")));
}

이러한 메서드에 대한 단위 테스트를 작성하면 오류가 발생하기 쉽습니다. 왜냐하면 우리가 일부 극단적인 경우를 잊어버리고 기본적으로 모든 가능한 시나리오를 다루지 않을 가능성이 높기 때문입니다.

다행스럽게도 우리는 모든 경우를 다룰 수 있는 속성 기반 테스트를 작성할 수 있습니다. 먼저 테스트에 어떤 종류의 숫자를 입력해야 하는지 정의해야 합니다.

Arbitrary<Integer> multiplesOf2 = Arbitrary.integer()
  .filter(i -> i > 0)
  .filter(i -> i % 2 == 0 && i % 5 != 0);

우리는 입력 숫자가 두 가지 조건을 충족해야 한다고 지정했습니다. 즉, 0보다 커야 하고 나머지 없이 2로 나눌 수 있어야 하지만 5로는 나눌 수 없어야 합니다.

다음으로 테스트된 함수가 주어진 인수에 대해 적절한 값을 반환하는지 확인하는 조건을 정의해야 합니다.

CheckedFunction1<Integer, Boolean> mustEquals
  = i -> stringsSupplier().get(i).equals("DividedByTwoWithoutRemainder");

속성 기반 테스트를 시작하려면 Property 클래스 를 사용해야 합니다 .

CheckResult result = Property
  .def("Every second element must equal to DividedByTwoWithoutRemainder")
  .forAll(multiplesOf2)
  .suchThat(mustEquals)
  .check(10_000, 100);

result.assertIsSatisfied();

우리는 2의 배수인 모든 랜덤의 정수에 대해 mustEquals 술어가 충족되어야 한다고 지정하고 있습니다. check() 메서드는 생성된 입력 의 크기와 이 테스트가 실행될 횟수를 사용합니다.

나머지 없이 2와 5로 나눌 수 있는 모든 입력 숫자에 대해 stringsSupplier() 함수가 DividedByTwoAndFiveWithoutRemainder 문자열 을 반환 하는지 확인하는 또 다른 테스트를 빠르게 작성할 수 있습니다 .

임의 Provider와 CheckedFunction 을 변경해야 합니다 .

Arbitrary<Integer> multiplesOf5 = Arbitrary.integer()
  .filter(i -> i > 0)
  .filter(i -> i % 5 == 0 && i % 2 == 0);

CheckedFunction1<Integer, Boolean> mustEquals
  = i -> stringsSupplier().get(i).endsWith("DividedByTwoAndFiveWithoutRemainder");

그런 다음 속성 기반 테스트를 1,000회 반복 실행할 수 있습니다.

Property.def("Every fifth element must equal to DividedByTwoAndFiveWithoutRemainder")
  .forAll(multiplesOf5)
  .suchThat(mustEquals)
  .check(10_000, 1_000)
  .assertIsSatisfied();

5. 결론

이 빠른 기사에서 우리는 속성 기반 테스트의 개념을 살펴보았습니다.

우리는 vavr -test 라이브러리를 사용하여 테스트를 만들었습니다. Arbitrary, CheckedFunctionProperty 클래스를 사용 하여 vavr-test 를 사용하여 속성 기반 테스트를 정의했습니다 .

이러한 모든 예제와 코드 스니펫의 구현은 GitHub 에서 찾을 수 있습니다. 이것은 Maven 프로젝트이므로 있는 그대로 쉽게 가져오고 실행할 수 있어야 합니다.

Junit footer banner