Andrzej Jóźwiak
andrzej.jozwiak@gmail.com
https://github.com/artificialrevelations
Property
Based Tests
And Where
To Find Them
meetup.juglodz.pl
yt.juglodz.pl
fb.juglodz.pl
2
Exploits of a Mom: https://xkcd.com/327/ 3
Poll: Unit tests ...
A. are exhaustive
B. guarantee the correctness
C. help find unknown cases
D. all of the above
E. it really depends on the context
4
Would you like to play a game?
Can your unit tests ...
5
find out that the car brakes do not work
when volume on the radio is changed?
Can your unit tests ...
Experiences with QuickCheck: https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quviq-testing.pdf 6
Bypassing Android lockscreen: https://sites.utexas.edu/iso/2015/09/15/android-5-lockscreen-bypass/
find out that putting enough text in the
Android lock screen text field can crash it
and give full access to the phone?
Can your unit tests ...
7
A Formal Specification of a File Synchronizer: https://www.cis.upenn.edu/~bcpierce/papers/unisonspec.pdf
find out that your secure file storage is
not secure and is losing data?
Can your unit tests ...
8
find out that going back and forth through
a list of available voices can cause the
app to play an incorrect voice?
Can your unit tests ...
my own child sure can!
9
This is a thing that we found out in our own app
10
Twitter: https://twitter.com/cbirchall/status/1209156343904587779
All unit tests passing do not always guarantee correctness...
11
And now for an academic example...
public static <A> List<A> reverse(final List<A> original) {
final int size = original.size();
final List<A> reversed = new ArrayList<>(size);
// going from the last element to the first of the
original list
for (int i = size - 1; i >= 0; i--) {
// thus adding the elements in the reversed order
reversed.add(original.get(i));
}
return reversed;
}
What are the properties of the code here?
Github: https://git.io/JfDGP
12
Lists reverse function example based tests
Github: https://git.io/JfDGa
13
Let’s build an example property...
Github: https://git.io/JfDZS
@Test
public void reverse_reversed_equals_original() {
// given:
final List<String> tested = Arrays.asList("This", "is", "a", "test");
// when:
final List<String> result = reverse(reverse(tested));
// then:
assertEquals(tested, result);
// Should we create more examples?
}
14
Let’s build a property based testing framework ...
We will notice soon enough that something is missing
15
Let’s build a property based testing framework ...
private interface Property<A> {
boolean check(A value);
}
private static <A> Property<List<A>> reverseOfReversedEqualsOriginal ()
{
return original -> reverse(reverse(original)).equals(original);
}
Github: https://git.io/JfDZS
16
Let’s build a property based testing framework ...
private interface Generator<A> {
A generate(Random seed);
}
// A very simple generator that produces lists:
// - with size varying from 0 to 100
// - with elements being numbers from 0 to 100 as strings
private Generator<List<String>> randomStringListsGenerator () {
return seed -> {
final int randomSize = randInt(seed, 0, 100);
final List<String> randomizedList = new
ArrayList<>(randomSize);
for (int i = 0; i < randomSize; i++) {
randomizedList .add(String.valueOf(randInt(seed, 0, 100)));
}
return randomizedList ;
};
}
Github: https://git.io/JfDZS
17
Let’s build a property based testing framework ...
private static <A> void quickCheck(final long seedValue,
final int numberOfTries,
final Property<A> property,
final Generator<A> generator) {
final Random seed = new Random(seedValue);
for (int i = 0; i < numberOfTries; i++) {
final A tested = generator.generate(seed);
final boolean result = property.check(tested);
if (!result) {
final StringBuilder builder =
new StringBuilder()
.append("Property test failed")
.append("nSeed value: ")
.append(seedValue)
.append("nExample data: ")
.append(tested);
throw new AssertionError(builder);
}
}
}
Github: https://git.io/JfDZS
18
Let’s build a property based testing framework ...
@Test
public void custom_reverse_property_test() {
final int numberOfTries = 1000;
final long seedValue = new Random().nextLong();
final Property<List<String>> reverseProperty =
reverseOfReversedEqualsOriginal();
final Generator<List<String>> generator =
randomStringListsGenerator();
quickCheck(seedValue, numberOfTries, reverseProperty, generator);
}
Github: https://git.io/JfDZS
19
We are lucky! The example only has 19 elements, what
would happen if it had 1000 or 10000 elements?
private static <A> Property<List<A>> reverseWrongProperty() {
return original -> reverse(original).equals(original);
}
Github: https://git.io/JfDZS
java.lang.AssertionError: Property test failed
Seed value: -2569510089704470893
Example data: [40, 15, 20, 30, 36, 35, 55, 99, 89, 93, 67, 27,
31, 95, 26, 6, 84, 23, 92]
How can we know our little framework is correct?
20
What about a professional solution?
Github: https://git.io/JfDZS
@Property
@Report(Reporting.GENERATED)
public boolean broken_reverse_property
(@ForAll List<?> original) {
return Lists.reverse(original).equals(original);
}
|-------------------jqwik-------------------
tries = 1 | # of calls to property
checks = 1 | # of not rejected calls
generation-mode = RANDOMIZED | parameters are randomly generated
after-failure = PREVIOUS_SEED | use the previous seed
seed = -1616208483702989146 | random seed to reproduce generated values
sample = [[Any[0], Any[1]]]
original-sample = [[Any[64], Any[226], Any[319], Any[71], Any[351], Any[137],
Any[9], Any[262], Any[239], Any[485], Any[23], Any[265], Any[108], Any[348],
Any[202], Any[365], Any[147], Any[347], Any[133]]]
It also found a 19 element example but managed to shrink it to 2
21
Poll: Our framework was missing ...
A. a way to shrink the example to smallest possible
size
B. a way to shrink the values used by the example
C. pretty printing of the output
D. all of the above
E. nothing, It was perfect!
F. I bet there is something more!
22
Our framework was missing ...
Are there well established categories of
properties?
23
Before we leave the realm of academic examples...
and the Math behind them is minimal! I promise.
24
Property Category: Encode/Decode
25
Property Category: Encode/Decode
Twitter: https://github.com/btlines/pbdirect
26
Property Category: Encode/Decode
Twitter: https://github.com/btlines/pbdirect
27
Property Category: Invariants
28
Property Category: Invariants
size of the collection: some operations on collections do not change their size but
only modify the order of elements or their state like sorting, mapping
content of the collection: some operations on collections do not change their
content (do not remove or add elements), it’s possible to find all elements but just in a
different order
order of elements: some operations change the characteristics of elements but do
not change their order
domain specific: premium users will always have a special discount regardless of the
amount of items on their shopping cart. We can always find properties of our model
that do not changed regardless of operations applied.
29
Property Category: Idempotence
30
Property Category: Idempotence
absolute value of an integer: abs(abs(x)) = abs(x)
floor, ceiling functions: ceil(ceil(x)) = ceil(x)
database search: searching for an element in a database multiple times should give
us the same results each time. This only holds in a case where there are no updates in
between.
sorting: sorting a collection multiple times should give us the same result, sorting an
already sorted collection should give us the same result, the same applies for filtering,
searching for distinct elements etc.
crosswalk button: pressing a crosswalk button multiple times will not change the end
result of the lights changing from red to green.
31
Property Category: Different paths lead to the same destination
32
Property Category: Different paths lead to the same destination
commutative property of addition: x + y = y + x, for all x, y ∈ R
putting socks on your feet: does it matter in which order we will put our socks on? If
the thing that we would like end up with are both feet in socks, then it does not matter
if we start with left or right foot!
applying discount code to the shopping cart: does it matter if we apply the discount
code to an empty cart (ofc if the API allows to start shopping like this) before we add
the items? The result should be the same for when we apply the discount to already
filled shopping cart.
putting ingredients on your pizza: does it matter if we start with pineapple or ham if
we want to have great pizza? I know I know this example is controversial!
filtering and sorting the results: the end result should be the same if we start from
sorting and then filter out the unwanted results. Let’s not focus on the issue of
optimization
33
Property Category: Test Oracle
34
Property Category: Test Oracle
result should stay the same: code after refactoring should give the same results as
the original one for the same input. This can be applied for any kind of code not only
examples like sorting or filtering. If we are reworking some UI elements to support
new cases it should behave the same like the old one for all old cases.
the new algorithm should not have worse performance then the old one: if the
refactored code should use less memory, use less hard drive space, perform less
operations, in other words just be better in some way or at least not worse, then
comparing old with new is the way to go.
when writing a property is not obvious: instead of finding a property of the code like
encode/decode or different paths same result, we can base our tests on the already
existing code and its results.
OK. Everything is nice but I am not writing
sorting for my day job!
35
Now off to the real world...
or reverse, or map, or json encoding for that matter
36
1. open new database
2. put key1 and val1
3. close database
4. open database
5. delete key2
6. delete key1
7. close database
8. open database
9. delete key2
10. close database
11. open database
12. put key3 and val1
13. close database
14. open database
15. close database
16. open database
17. seek first
Expected output? key3
Actual output? key1
Reappearing "ghost" key after 17 steps: https://github.com/google/leveldb/issues/50
A ghost story ...
Would you come up with such an example?
37
Reappearing "ghost" key after 17 steps: https://github.com/google/leveldb/issues/50
A ghost story - a rehaunting ...
After a patch that was supposedly fixing the issue a new example was found
this time made up from 33 steps. After almost a month it was finally fixed.
Fixed bug in picking inputs for a level-0 compaction.
When finding overlapping files, the covered range may expand as files are added
to the input set. We now correctly expand the range when this happens instead
of continuing to use the old range. This change fixes a bug related to deleted
keys showing up incorrectly after a compaction.
38
Stateful testing...
39
Stateful testing...
Model - describes the current expected state of the system. Often we know what to
expect from the code, it does not matter how the code gets to that result.
Commands - represent what the system under test should do, command generation
needs to take into account the current model state.
Validation - way to check the current model state and compare it with the system
under test state.
SUT - system under test, stateful in it’s nature
40
A stateful representation of a shopping cart ...
public class ShoppingCart {
public Quantity get(Product product)
public List<Product> getAllProducts ()
public Quantity add(Product product, Quantity quantity)
public Quantity remove(Product product, Quantity quantity)
public void clear()
public void setDiscount(DiscountCode code)
public void clearDiscount()
public Price getTotalPrice()
public Quantity getTotalQuantity ()
}
41
A simplified shopping cart model ...
public class ShoppingCartModel {
private List<ShoppingCartModelElement> products = new
LinkedList<>();
private int discount = 0; //%
private static class ShoppingCartModelElement {
public String product;
public int price;
public int quantity;
}
}
42
A simplified representation of a command in our system ...
public interface Command<M, S> {
boolean preconditions(final M model);
void execute(final M model, final S sut);
void postconditions (final M model, final S sut);
}
43
An implementation of one of the available commands ...
public class AddProductCommand implements Command<ShoppingCartModel , ShoppingCart >
{
public void execute(final ShoppingCartModel model,
final ShoppingCart sut) {
model.addProduct(
product .getName(),
quantity .getValue(),
product .getPrice().getValue()
);
sut.add( product, quantity);
}
public void postconditions (final ShoppingCartModel model,
final ShoppingCart sut) {
Assertions.assertEquals(
model.getQuantity( product.getName()),
sut.get( product).getValue()
);
}
44
Available commands for our property test
- AddProductCommand
- RemoveProductCommand
- GetProductCommand
- GetAllProductsCommand
- ClearProductCommand
- ClearCommand
- ClearProductCommand
- SetDiscountCommand
- ClearDiscountCommand
- GetTotalPriceCommand
- GetTotalQuantityCommand
Now we need to create random sequences of commands that will stress the
implementation of our stateful component.
45
Testing properties of a shopping cart ...
Let’s introduce a bug in the shopping cart:
- if the shopping carts has 3 or more elements
- it will NOT add any more elements
Run failed after following actions:
AddProductCommand{ product=Product{name='AAA', price=Price(3)},
quantity=Quantity(1)}
AddProductCommand{ product=Product{name='AAa', price=Price(3)},
quantity=Quantity(1)}
AddProductCommand{ product=Product{name='ABA', price=Price(3)},
quantity=Quantity(1)}
AddProductCommand{ product=Product{name='AAB', price=Price(3)},
quantity=Quantity(1)}
46
Testing properties of a shopping cart ...
original-sample = [ActionSequence[FAILED]: [SetDiscountCommand{discount=Discount(58)},
GetTotalQuantityCommand{}, ClearCommand{}, SetDiscountCommand{discount=Discount(100)},
SetDiscountCommand{discount=Discount(58)}, GetTotalQuantityCommand{},
GetTotalPriceCommand{}, ClearDiscountCommand{}, SetDiscountCommand{discount=Discount(18)},
GetTotalQuantityCommand{}, GetTotalPriceCommand{}, ClearCommand{},
GetTotalQuantityCommand{}, AddProductCommand{ product=Product{name='ZzZaWB',
price=Price(6)}, quantity=Quantity(60)}, GetTotalPriceCommand{}, AddProductCommand{
product=Product{name='zEfaZZga', price=Price(8)}, quantity=Quantity(74)},
GetTotalPriceCommand{}, ClearDiscountCommand{}, AddProductCommand{
product=Product{name='faGZQAGAQ', price=Price(9)}, quantity=Quantity(154)},
GetTotalQuantityCommand{}, AddProductCommand{ product=Product{name='ZVdJaj',
price=Price(6)}, quantity=Quantity(66)}, SetDiscountCommand{discount=Discount(32)},
GetTotalQuantityCommand{}, GetTotalQuantityCommand{}, GetTotalQuantityCommand{},
AddProductCommand{ product=Product{name='AAzZza', price=Price(6)},
quantity=Quantity(5)}]]
47
Thesis Defense: https://xkcd.com/1403/
Q&A time ...
48
Further reading:
(book) PropEr Testing https://propertesting.com/toc.html
(documentation) QuickCheck: https://hackage.haskell.org/package/QuickCheck
(article) Introduction to PBT: https://fsharpforfunandprofit.com/posts/property-based-testing/
(documentation) jqwik: https://jqwik.net/
(presentation) Stateful PBT: https://www.youtube.com/watch?v=owHmYA52SIM
(presentation) Don’t Write Tests: https://www.youtube.com/watch?v=hXnS_Xjwk2Y
(presentation) Testing the Hard Stuff: https://www.youtube.com/watch?v=zi0rHwfiX1Q
(examples): https://github.com/artificialrevelations/property-based-testing-workshop

More Related Content

PDF
Google guava
PDF
The core libraries you always wanted - Google Guava
PPTX
Introduction to CDI and DI in Java EE 6
PDF
Dependency Injection with CDI in 15 minutes
PDF
Clean code with google guava jee conf
PDF
Google Guava - Core libraries for Java & Android
PDF
Google Guava
PDF
Google guava - almost everything you need to know
Google guava
The core libraries you always wanted - Google Guava
Introduction to CDI and DI in Java EE 6
Dependency Injection with CDI in 15 minutes
Clean code with google guava jee conf
Google Guava - Core libraries for Java & Android
Google Guava
Google guava - almost everything you need to know

What's hot (20)

PDF
Google Guava for cleaner code
KEY
Google Guava
PDF
ppopoff
PDF
Elixir: the not-so-hidden path to Erlang
PDF
Functional programming's relentless bug hunter claire
PDF
An Introduction to the World of Testing for Front-End Developers
PDF
FITC Web Unleashed 2017 - Introduction to the World of Testing for Front-End ...
PDF
JavaScript in 2016
ODP
From object oriented to functional domain modeling
PPT
Bye Bye Charles, Welcome Odo, Android Meetup Berlin May 2014
PDF
Android testing
PDF
Stuff you didn't know about action script
PDF
Object Oriented JavaScript
PDF
Advanced Debugging Using Java Bytecodes
PDF
Swift 2
PDF
Twins: Object Oriented Programming and Functional Programming
PDF
Don't do this
PDF
Thirteen ways of looking at a turtle
PPTX
Use of Apache Commons and Utilities
PPTX
Akka in-action
Google Guava for cleaner code
Google Guava
ppopoff
Elixir: the not-so-hidden path to Erlang
Functional programming's relentless bug hunter claire
An Introduction to the World of Testing for Front-End Developers
FITC Web Unleashed 2017 - Introduction to the World of Testing for Front-End ...
JavaScript in 2016
From object oriented to functional domain modeling
Bye Bye Charles, Welcome Odo, Android Meetup Berlin May 2014
Android testing
Stuff you didn't know about action script
Object Oriented JavaScript
Advanced Debugging Using Java Bytecodes
Swift 2
Twins: Object Oriented Programming and Functional Programming
Don't do this
Thirteen ways of looking at a turtle
Use of Apache Commons and Utilities
Akka in-action
Ad

Similar to Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webinar June 2020 (20)

ODP
New Ideas for Old Code - Greach
PPTX
Test-Driven Design Insights@DevoxxBE 2023.pptx
PDF
Factories, mocks and spies: a tester's little helpers
PDF
React Native One Day
PDF
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
PDF
JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...
PPT
Mockito with a hint of PowerMock
PDF
Unit testing - A&BP CC
PDF
Fuzzing: The New Unit Testing
PDF
Spock: Test Well and Prosper
PPTX
Finding bugs that matter with Findbugs
ODP
Static Analysis in IDEA
PPTX
Android Unit Test
PPTX
Building unit tests correctly
PPTX
presentation des google mock dans un contexte de tdd
PPTX
White-box Unit Test Generation with Microsoft IntelliTest
PDF
An Introduction to Property Based Testing
DOCX
1 COMP 182 Fall 2016 Project 6 Binary Search Trees .docx
PDF
Atlassian Groovy Plugins
PDF
Testing and validating distributed systems with Apache Spark and Apache Beam ...
New Ideas for Old Code - Greach
Test-Driven Design Insights@DevoxxBE 2023.pptx
Factories, mocks and spies: a tester's little helpers
React Native One Day
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...
Mockito with a hint of PowerMock
Unit testing - A&BP CC
Fuzzing: The New Unit Testing
Spock: Test Well and Prosper
Finding bugs that matter with Findbugs
Static Analysis in IDEA
Android Unit Test
Building unit tests correctly
presentation des google mock dans un contexte de tdd
White-box Unit Test Generation with Microsoft IntelliTest
An Introduction to Property Based Testing
1 COMP 182 Fall 2016 Project 6 Binary Search Trees .docx
Atlassian Groovy Plugins
Testing and validating distributed systems with Apache Spark and Apache Beam ...
Ad

More from Andrzej Jóźwiak (7)

PDF
Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...
PDF
Does testability imply good design - Andrzej Jóźwiak - TomTom Dev Day 2022
PDF
Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...
PDF
Capability Driven Design - Andrzej Jóźwiak - TomTom Dev Day 2021
PDF
Types of Randomness in Game Design - Rapid Talks - December 2020
PDF
Capability Driven Design - Rapid Talks - November 2020
PPTX
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020
Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...
Does testability imply good design - Andrzej Jóźwiak - TomTom Dev Day 2022
Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...
Capability Driven Design - Andrzej Jóźwiak - TomTom Dev Day 2021
Types of Randomness in Game Design - Rapid Talks - December 2020
Capability Driven Design - Rapid Talks - November 2020
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020

Recently uploaded (20)

PDF
Consumable AI The What, Why & How for Small Teams.pdf
PPT
Galois Field Theory of Risk: A Perspective, Protocol, and Mathematical Backgr...
PDF
A Late Bloomer's Guide to GenAI: Ethics, Bias, and Effective Prompting - Boha...
PPTX
Custom Battery Pack Design Considerations for Performance and Safety
PPTX
AI IN MARKETING- PRESENTED BY ANWAR KABIR 1st June 2025.pptx
PDF
TrustArc Webinar - Click, Consent, Trust: Winning the Privacy Game
PDF
Hybrid horned lizard optimization algorithm-aquila optimizer for DC motor
PDF
From MVP to Full-Scale Product A Startup’s Software Journey.pdf
PPTX
Microsoft Excel 365/2024 Beginner's training
PPTX
Chapter 5: Probability Theory and Statistics
PDF
Flame analysis and combustion estimation using large language and vision assi...
PPT
Geologic Time for studying geology for geologist
PDF
Taming the Chaos: How to Turn Unstructured Data into Decisions
PDF
A contest of sentiment analysis: k-nearest neighbor versus neural network
PDF
sbt 2.0: go big (Scala Days 2025 edition)
PDF
sustainability-14-14877-v2.pddhzftheheeeee
PPTX
Benefits of Physical activity for teenagers.pptx
PDF
1 - Historical Antecedents, Social Consideration.pdf
PDF
OpenACC and Open Hackathons Monthly Highlights July 2025
PDF
Developing a website for English-speaking practice to English as a foreign la...
Consumable AI The What, Why & How for Small Teams.pdf
Galois Field Theory of Risk: A Perspective, Protocol, and Mathematical Backgr...
A Late Bloomer's Guide to GenAI: Ethics, Bias, and Effective Prompting - Boha...
Custom Battery Pack Design Considerations for Performance and Safety
AI IN MARKETING- PRESENTED BY ANWAR KABIR 1st June 2025.pptx
TrustArc Webinar - Click, Consent, Trust: Winning the Privacy Game
Hybrid horned lizard optimization algorithm-aquila optimizer for DC motor
From MVP to Full-Scale Product A Startup’s Software Journey.pdf
Microsoft Excel 365/2024 Beginner's training
Chapter 5: Probability Theory and Statistics
Flame analysis and combustion estimation using large language and vision assi...
Geologic Time for studying geology for geologist
Taming the Chaos: How to Turn Unstructured Data into Decisions
A contest of sentiment analysis: k-nearest neighbor versus neural network
sbt 2.0: go big (Scala Days 2025 edition)
sustainability-14-14877-v2.pddhzftheheeeee
Benefits of Physical activity for teenagers.pptx
1 - Historical Antecedents, Social Consideration.pdf
OpenACC and Open Hackathons Monthly Highlights July 2025
Developing a website for English-speaking practice to English as a foreign la...

Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webinar June 2020

  • 3. Exploits of a Mom: https://xkcd.com/327/ 3
  • 4. Poll: Unit tests ... A. are exhaustive B. guarantee the correctness C. help find unknown cases D. all of the above E. it really depends on the context 4
  • 5. Would you like to play a game? Can your unit tests ... 5
  • 6. find out that the car brakes do not work when volume on the radio is changed? Can your unit tests ... Experiences with QuickCheck: https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quviq-testing.pdf 6
  • 7. Bypassing Android lockscreen: https://sites.utexas.edu/iso/2015/09/15/android-5-lockscreen-bypass/ find out that putting enough text in the Android lock screen text field can crash it and give full access to the phone? Can your unit tests ... 7
  • 8. A Formal Specification of a File Synchronizer: https://www.cis.upenn.edu/~bcpierce/papers/unisonspec.pdf find out that your secure file storage is not secure and is losing data? Can your unit tests ... 8
  • 9. find out that going back and forth through a list of available voices can cause the app to play an incorrect voice? Can your unit tests ... my own child sure can! 9 This is a thing that we found out in our own app
  • 11. 11 And now for an academic example... public static <A> List<A> reverse(final List<A> original) { final int size = original.size(); final List<A> reversed = new ArrayList<>(size); // going from the last element to the first of the original list for (int i = size - 1; i >= 0; i--) { // thus adding the elements in the reversed order reversed.add(original.get(i)); } return reversed; } What are the properties of the code here? Github: https://git.io/JfDGP
  • 12. 12 Lists reverse function example based tests Github: https://git.io/JfDGa
  • 13. 13 Let’s build an example property... Github: https://git.io/JfDZS @Test public void reverse_reversed_equals_original() { // given: final List<String> tested = Arrays.asList("This", "is", "a", "test"); // when: final List<String> result = reverse(reverse(tested)); // then: assertEquals(tested, result); // Should we create more examples? }
  • 14. 14 Let’s build a property based testing framework ... We will notice soon enough that something is missing
  • 15. 15 Let’s build a property based testing framework ... private interface Property<A> { boolean check(A value); } private static <A> Property<List<A>> reverseOfReversedEqualsOriginal () { return original -> reverse(reverse(original)).equals(original); } Github: https://git.io/JfDZS
  • 16. 16 Let’s build a property based testing framework ... private interface Generator<A> { A generate(Random seed); } // A very simple generator that produces lists: // - with size varying from 0 to 100 // - with elements being numbers from 0 to 100 as strings private Generator<List<String>> randomStringListsGenerator () { return seed -> { final int randomSize = randInt(seed, 0, 100); final List<String> randomizedList = new ArrayList<>(randomSize); for (int i = 0; i < randomSize; i++) { randomizedList .add(String.valueOf(randInt(seed, 0, 100))); } return randomizedList ; }; } Github: https://git.io/JfDZS
  • 17. 17 Let’s build a property based testing framework ... private static <A> void quickCheck(final long seedValue, final int numberOfTries, final Property<A> property, final Generator<A> generator) { final Random seed = new Random(seedValue); for (int i = 0; i < numberOfTries; i++) { final A tested = generator.generate(seed); final boolean result = property.check(tested); if (!result) { final StringBuilder builder = new StringBuilder() .append("Property test failed") .append("nSeed value: ") .append(seedValue) .append("nExample data: ") .append(tested); throw new AssertionError(builder); } } } Github: https://git.io/JfDZS
  • 18. 18 Let’s build a property based testing framework ... @Test public void custom_reverse_property_test() { final int numberOfTries = 1000; final long seedValue = new Random().nextLong(); final Property<List<String>> reverseProperty = reverseOfReversedEqualsOriginal(); final Generator<List<String>> generator = randomStringListsGenerator(); quickCheck(seedValue, numberOfTries, reverseProperty, generator); } Github: https://git.io/JfDZS
  • 19. 19 We are lucky! The example only has 19 elements, what would happen if it had 1000 or 10000 elements? private static <A> Property<List<A>> reverseWrongProperty() { return original -> reverse(original).equals(original); } Github: https://git.io/JfDZS java.lang.AssertionError: Property test failed Seed value: -2569510089704470893 Example data: [40, 15, 20, 30, 36, 35, 55, 99, 89, 93, 67, 27, 31, 95, 26, 6, 84, 23, 92] How can we know our little framework is correct?
  • 20. 20 What about a professional solution? Github: https://git.io/JfDZS @Property @Report(Reporting.GENERATED) public boolean broken_reverse_property (@ForAll List<?> original) { return Lists.reverse(original).equals(original); } |-------------------jqwik------------------- tries = 1 | # of calls to property checks = 1 | # of not rejected calls generation-mode = RANDOMIZED | parameters are randomly generated after-failure = PREVIOUS_SEED | use the previous seed seed = -1616208483702989146 | random seed to reproduce generated values sample = [[Any[0], Any[1]]] original-sample = [[Any[64], Any[226], Any[319], Any[71], Any[351], Any[137], Any[9], Any[262], Any[239], Any[485], Any[23], Any[265], Any[108], Any[348], Any[202], Any[365], Any[147], Any[347], Any[133]]] It also found a 19 element example but managed to shrink it to 2
  • 21. 21 Poll: Our framework was missing ... A. a way to shrink the example to smallest possible size B. a way to shrink the values used by the example C. pretty printing of the output D. all of the above E. nothing, It was perfect! F. I bet there is something more!
  • 22. 22 Our framework was missing ...
  • 23. Are there well established categories of properties? 23 Before we leave the realm of academic examples... and the Math behind them is minimal! I promise.
  • 25. 25 Property Category: Encode/Decode Twitter: https://github.com/btlines/pbdirect
  • 26. 26 Property Category: Encode/Decode Twitter: https://github.com/btlines/pbdirect
  • 28. 28 Property Category: Invariants size of the collection: some operations on collections do not change their size but only modify the order of elements or their state like sorting, mapping content of the collection: some operations on collections do not change their content (do not remove or add elements), it’s possible to find all elements but just in a different order order of elements: some operations change the characteristics of elements but do not change their order domain specific: premium users will always have a special discount regardless of the amount of items on their shopping cart. We can always find properties of our model that do not changed regardless of operations applied.
  • 30. 30 Property Category: Idempotence absolute value of an integer: abs(abs(x)) = abs(x) floor, ceiling functions: ceil(ceil(x)) = ceil(x) database search: searching for an element in a database multiple times should give us the same results each time. This only holds in a case where there are no updates in between. sorting: sorting a collection multiple times should give us the same result, sorting an already sorted collection should give us the same result, the same applies for filtering, searching for distinct elements etc. crosswalk button: pressing a crosswalk button multiple times will not change the end result of the lights changing from red to green.
  • 31. 31 Property Category: Different paths lead to the same destination
  • 32. 32 Property Category: Different paths lead to the same destination commutative property of addition: x + y = y + x, for all x, y ∈ R putting socks on your feet: does it matter in which order we will put our socks on? If the thing that we would like end up with are both feet in socks, then it does not matter if we start with left or right foot! applying discount code to the shopping cart: does it matter if we apply the discount code to an empty cart (ofc if the API allows to start shopping like this) before we add the items? The result should be the same for when we apply the discount to already filled shopping cart. putting ingredients on your pizza: does it matter if we start with pineapple or ham if we want to have great pizza? I know I know this example is controversial! filtering and sorting the results: the end result should be the same if we start from sorting and then filter out the unwanted results. Let’s not focus on the issue of optimization
  • 34. 34 Property Category: Test Oracle result should stay the same: code after refactoring should give the same results as the original one for the same input. This can be applied for any kind of code not only examples like sorting or filtering. If we are reworking some UI elements to support new cases it should behave the same like the old one for all old cases. the new algorithm should not have worse performance then the old one: if the refactored code should use less memory, use less hard drive space, perform less operations, in other words just be better in some way or at least not worse, then comparing old with new is the way to go. when writing a property is not obvious: instead of finding a property of the code like encode/decode or different paths same result, we can base our tests on the already existing code and its results.
  • 35. OK. Everything is nice but I am not writing sorting for my day job! 35 Now off to the real world... or reverse, or map, or json encoding for that matter
  • 36. 36 1. open new database 2. put key1 and val1 3. close database 4. open database 5. delete key2 6. delete key1 7. close database 8. open database 9. delete key2 10. close database 11. open database 12. put key3 and val1 13. close database 14. open database 15. close database 16. open database 17. seek first Expected output? key3 Actual output? key1 Reappearing "ghost" key after 17 steps: https://github.com/google/leveldb/issues/50 A ghost story ... Would you come up with such an example?
  • 37. 37 Reappearing "ghost" key after 17 steps: https://github.com/google/leveldb/issues/50 A ghost story - a rehaunting ... After a patch that was supposedly fixing the issue a new example was found this time made up from 33 steps. After almost a month it was finally fixed. Fixed bug in picking inputs for a level-0 compaction. When finding overlapping files, the covered range may expand as files are added to the input set. We now correctly expand the range when this happens instead of continuing to use the old range. This change fixes a bug related to deleted keys showing up incorrectly after a compaction.
  • 39. 39 Stateful testing... Model - describes the current expected state of the system. Often we know what to expect from the code, it does not matter how the code gets to that result. Commands - represent what the system under test should do, command generation needs to take into account the current model state. Validation - way to check the current model state and compare it with the system under test state. SUT - system under test, stateful in it’s nature
  • 40. 40 A stateful representation of a shopping cart ... public class ShoppingCart { public Quantity get(Product product) public List<Product> getAllProducts () public Quantity add(Product product, Quantity quantity) public Quantity remove(Product product, Quantity quantity) public void clear() public void setDiscount(DiscountCode code) public void clearDiscount() public Price getTotalPrice() public Quantity getTotalQuantity () }
  • 41. 41 A simplified shopping cart model ... public class ShoppingCartModel { private List<ShoppingCartModelElement> products = new LinkedList<>(); private int discount = 0; //% private static class ShoppingCartModelElement { public String product; public int price; public int quantity; } }
  • 42. 42 A simplified representation of a command in our system ... public interface Command<M, S> { boolean preconditions(final M model); void execute(final M model, final S sut); void postconditions (final M model, final S sut); }
  • 43. 43 An implementation of one of the available commands ... public class AddProductCommand implements Command<ShoppingCartModel , ShoppingCart > { public void execute(final ShoppingCartModel model, final ShoppingCart sut) { model.addProduct( product .getName(), quantity .getValue(), product .getPrice().getValue() ); sut.add( product, quantity); } public void postconditions (final ShoppingCartModel model, final ShoppingCart sut) { Assertions.assertEquals( model.getQuantity( product.getName()), sut.get( product).getValue() ); }
  • 44. 44 Available commands for our property test - AddProductCommand - RemoveProductCommand - GetProductCommand - GetAllProductsCommand - ClearProductCommand - ClearCommand - ClearProductCommand - SetDiscountCommand - ClearDiscountCommand - GetTotalPriceCommand - GetTotalQuantityCommand Now we need to create random sequences of commands that will stress the implementation of our stateful component.
  • 45. 45 Testing properties of a shopping cart ... Let’s introduce a bug in the shopping cart: - if the shopping carts has 3 or more elements - it will NOT add any more elements Run failed after following actions: AddProductCommand{ product=Product{name='AAA', price=Price(3)}, quantity=Quantity(1)} AddProductCommand{ product=Product{name='AAa', price=Price(3)}, quantity=Quantity(1)} AddProductCommand{ product=Product{name='ABA', price=Price(3)}, quantity=Quantity(1)} AddProductCommand{ product=Product{name='AAB', price=Price(3)}, quantity=Quantity(1)}
  • 46. 46 Testing properties of a shopping cart ... original-sample = [ActionSequence[FAILED]: [SetDiscountCommand{discount=Discount(58)}, GetTotalQuantityCommand{}, ClearCommand{}, SetDiscountCommand{discount=Discount(100)}, SetDiscountCommand{discount=Discount(58)}, GetTotalQuantityCommand{}, GetTotalPriceCommand{}, ClearDiscountCommand{}, SetDiscountCommand{discount=Discount(18)}, GetTotalQuantityCommand{}, GetTotalPriceCommand{}, ClearCommand{}, GetTotalQuantityCommand{}, AddProductCommand{ product=Product{name='ZzZaWB', price=Price(6)}, quantity=Quantity(60)}, GetTotalPriceCommand{}, AddProductCommand{ product=Product{name='zEfaZZga', price=Price(8)}, quantity=Quantity(74)}, GetTotalPriceCommand{}, ClearDiscountCommand{}, AddProductCommand{ product=Product{name='faGZQAGAQ', price=Price(9)}, quantity=Quantity(154)}, GetTotalQuantityCommand{}, AddProductCommand{ product=Product{name='ZVdJaj', price=Price(6)}, quantity=Quantity(66)}, SetDiscountCommand{discount=Discount(32)}, GetTotalQuantityCommand{}, GetTotalQuantityCommand{}, GetTotalQuantityCommand{}, AddProductCommand{ product=Product{name='AAzZza', price=Price(6)}, quantity=Quantity(5)}]]
  • 48. 48 Further reading: (book) PropEr Testing https://propertesting.com/toc.html (documentation) QuickCheck: https://hackage.haskell.org/package/QuickCheck (article) Introduction to PBT: https://fsharpforfunandprofit.com/posts/property-based-testing/ (documentation) jqwik: https://jqwik.net/ (presentation) Stateful PBT: https://www.youtube.com/watch?v=owHmYA52SIM (presentation) Don’t Write Tests: https://www.youtube.com/watch?v=hXnS_Xjwk2Y (presentation) Testing the Hard Stuff: https://www.youtube.com/watch?v=zi0rHwfiX1Q (examples): https://github.com/artificialrevelations/property-based-testing-workshop