# Property-based testing

## Magda Stożek

### Let your testing library work for you

```Property-based testing: テストライブラリ活用方法
```
``def calculateDiscount(customer: Customer, now: LocalDate): BigDecimal``

Example: Calculating discount

Years:

0-1

1-2

2 +

0%

30%

10%

20%

birthday

``````case class Customer(
name: String,
joinedAt: LocalDate,
dateOfBirth: LocalDate
)``````
`顧客の継続年数によって割引率を計算する`

### Example-based tests

``it "should calculate no didcount" in {...}``
``it should "calculate 10 percent discount" in {...}``
``it should "calculate 20 percent discount" in {...}``
``it should "calculate birthday discount" in {...}``
``it "should calculate 20 percent discount for 3 years" in {...}``

Happy path

Edge cases

`例ベースのテストならこんな感じ`

`プロパティベーステストの世界では、これがどのように異なるか見てみましょう`
``def calculateDiscount(customer: Customer, now: LocalDate): BigDecimal``

Example: Calculating discount

Years:

0-1

1-2

2 +

0%

30%

10%

20%

birthday

``````case class Customer(
name: String,
joinedAt: LocalDate,
dateOfBirth: LocalDate
)``````
`どんなルールがあるか考えてみましょう`

### Property-based tests

``it "should not be lower than 0" in {...}``
``it "should not be over 30 percent" in {...}``
``it "should be proportional to membership years" in {...}``

Properties:

``it "should not be greater than birthday discount" in {...}``
`四つのルール`
``````property("Discount should not be over 30 percent") {
forAll { (customer: Customer, now: LocalDate) =>
val discount = calculator.calculateDiscount(customer, now)
discount should be <= BigDecimal(0.3)
}
}``````

Property: discount is never larger than 30%

`プロパティベーステストの書き方`
``````ScalaTestFailureLocation: pbt.DiscountCalculatorPropertySpec at (DiscountCalculatorPropertySpec.scala:28)
org.scalatest.exceptions.GeneratorDrivenPropertyCheckFailedException: DateTimeException was thrown during property evaluation.
Message: Invalid date 'February 29' as '2049' is not a leap year
Occurred when passed generated values (
arg0 = Customer(nWtCZLoAQ,2009-04-14,1988-02-29),
arg1 = Customer(bLNkLdYmCYCjVJlIDRWumkWZceQHyoulrCcietBLPyfBCqbyXqub,2015-12-19,1996-12-13),
arg2 = 2049-04-09
)``````

Discovered error

`見つかったエラーの例`

# Generators

`データを生成する`

Custom generators

``````val customerGen: Gen[Customer] = for {
name <- Gen.alphaStr
age <- Gen.choose(12, 120)
subscription <- Gen.option(Gen.oneOf("Daily", "Monthly", "Yearly")
dateJoined <- joinedGen()
} yield Customer(name, age, subscription, dateJoined)``````
`カスタムクラスのジェネレーター`

# Challenges

`プロパティベーステストの課題`

## Difficult questions earlier

Challenge #1

`難題が前倒しになります`

## Nondeterminism

Challenge #2

`ランダム性があるため、バグが後から見つかる可能性があります`

## Coming up with properties

Challenge #3

`どんなルールがあるか考えてみましょう`

# Strategies

1. Examples first, properties later

2. Properties for key components

```(1)例ベーステストから書きます、それからプロパティベーステスト
(2)重要なコンポーネントだけプロパティベーステストを書きます```

# Summary

1. Higher cost, higher value
2. Edge cases and misconceptions
3. Coming up with properties
4. Balance
```(1)高いコストだが高品質
(2)エッジケースと誤解を見つける
(3)プロパティを思いつけるようにする
(4)二種類のテストのバランスを取る```

## More

`参考資料`

### Questions?

``````property("End of presentation") {
forAll { (attendee: Attendee) =>
whenever(attendee.hasQuestions) {
`ご清聴ありがとうございました`