Testowanie oparte na właściwościach
Magda Stożek
BigDecimal calculateDiscount(Customer customer, LocalDate now);
Przykład: Liczenie zniżki
Staż:
0-1
1-2
2 +
0%
30%
10%
20%
urodziny
class Customer {
private final String name;
private final LocalDate joinedAt;
private final LocalDate dateOfBirth;
}
Zniżka:
Testy oparte na przykładach
@Test
void shouldCalculateNoDiscount() {...}
@Test
void shouldCalculate10percentDiscount() {...}
@Test
void shouldCalculate20percentDiscount() {...}
@Test
void shouldCalculateBirthdayDiscount() {...}
@Test
void shouldCalculate20percentDiscountFor3Years() {...}
Oczekiwany scenariusz:
Przypadki brzegowe:
A w języku właściwości?
BigDecimal calculateDiscount(Customer customer, LocalDate now);
Przykład: Liczenie zniżki
Staż:
0-1
1-2
2 +
0%
30%
10%
20%
urodziny
class Customer {
private final String name;
private final LocalDate joinedAt;
private final LocalDate dateOfBirth;
}
Zniżka:
Biblioteki
- Początek:
- 1999: QuickCheck (Haskell) / Quviq Quickcheck (Erlang)
- Potem:
-
C, C++, C#, Chicken, Clojure, Common Lisp, D, Elm, Elixir, Erlang, F#, Factor, Go, Io, Java, JavaScript, Julia, Kotlin, Logtalk, Lua, Node.js, Objective-C, OCaml, Perl, Prolog, PHP, Pony, Python, R, Racket, Ruby, Rust, Scala, Scheme, Smalltalk, Standard ML, Swift, Typescript, VB.NET, ...
-
Co dla Javy?
- junit-quickcheck (2013, JUnit 4)
- QuickTheories (2015, niezależne od frameworka, DSL)
-
jqwik (2017, JUnit 5)
@Property
boolean discountShouldNotBeOver30percent(@ForAll("validCustomer") Customer customer, @ForAll("futureDate") LocalDate now) {
BigDecimal discount = calculator.calculateDiscount(customer, now);
return discount.compareTo(new BigDecimal("0.3")) <= 0;
}
Właściwość: zniżka nigdy nie przekracza 30%
@Property
boolean shouldNotBeOver30percent(
@ForAll Customer customer,
@ForAll("futureDate") LocalDate now)
{
BigDecimal discount = calculator.calculateDiscount(customer, now);
Assertions.assertThat(discount).compareTo(new BigDecimal("0.3")) <= 0;
}
|-------------------jqwik-------------------
tries = 374 | # of calls to property
checks = 374 | # of not rejected calls
generation-mode = RANDOMIZED | parameters are randomly generated
after-failure = PREVIOUS_SEED | use the previous seed
seed = -272292185905571017 | random seed to reproduce generated values
sample =
[Customer{name='AAA', joinedAt=2010-01-01, dateOfBirth=1980-02-29}, 2019-10-06]
original-sample =
[Customer{name='DtQDpPfEx', joinedAt=2010-04-21, dateOfBirth=1980-02-29}, 2185-03-17]
Wykryty błąd
Wyzwania
Trudne pytania wcześniej
Wyzwanie #1
Niedeterminizm
Wyzwanie #2
Wymyślanie właściwości
Wyzwanie #3
Strategie
- najpierw przykłady, potem właściwości
- właściwości dla kluczowych elementów
Podsumowanie
- większy koszt, większa korzyść
- przypadki brzegowe i luki w rozumowaniu
- wymyślanie właściwości
- proporcje
Więcej
@Property
boolean endOfPresentation(@ForAll Attendee attendee) {
Assert.that(attendee.wantsToTryIt);
}
Testowanie oparte na właściwościach [Java, 30 min]
By Magda Stożek
Testowanie oparte na właściwościach [Java, 30 min]
- 518