SlideShare a Scribd company logo
1 of 105
Download to read offline
The lazy programmer's guide to
writing 1000's of tests
An introduction to property based testing
@ScottWlaschin
fsharpforfunandprofit.com
The NDC community right now
Part 1:
In which I have a conversation with a
remote developer
This was a project from a long time ago,
in a galaxy far far away
For some reason
we needed a
custom "add"
function
...some time later
Seriously, how *do* you know that
you have enough tests?
So I decide to start writing the
unit tests myself
[<Test>]
let ``When I add 1 + 3, I expect 4``()=
let result = add(1,3)
Assert.AreEqual(4,result)
[<Test>]
let ``When I add 2 + 2, I expect 4``()=
let result = add(2,2)
Assert.AreEqual(4,result)


First, I had a look at the existing tests...
[<Test>]
let ``When I add -1 + 3, I expect 2``()=
let result = add(-1,3)
Assert.AreEqual(2,result) 
Ok, now for my first new test...
let add(x,y) =
4
wtf!
Hmm.. let's look at the implementation...
[<Test>]
let ``When I add 2 + 3, I expect 5``()=
let result = add(2,3)
Assert.AreEqual(5,result)
[<Test>]
let ``When I add 1 + 41, I expect 42``()=
let result = add(1,41)
Assert.AreEqual(42,result)


Time for some more tests...
let add(x,y) =
match (x,y) with
| (2,3) -> 5
| (1,41) -> 42
| (_,_) -> 4 // all other cases
But let's just check the implementation again...
Write only enough code to
make the failing unit test pass.
http://www.javiersaldana.com/articles/tech/refactoring-the-three-laws-of-tdd
TDD best practices
[<Test>]
let ``When I add two numbers,
I expect to get their sum``()=
let testData = [
(1,2,3)
(2,2,4)
(3,5,8)
(27,15,42)
]
for (x,y,expected) in testData do
let actual = add(x,y)
Assert.AreEqual(expected,actual)
Another attempt at a test

let add(x,y) =
match (x,y) with
| (1,2) -> 3
| (2,3) -> 5
| (3,5) -> 8
| (1,41) -> 42
| (27,15) -> 42
| (_,_) -> 4 // all other cases
Let's check the implementation one more time....
It dawned on me who I was
dealing with...
...the legendary burned-out, always lazy and
often malicious programmer called...
The Enterprise
Developer From Hell
Rethinking the approach
The EDFH will always make
specific examples pass, no
matter what I do...
So let's not use
specific examples!
[<Test>]
let ``When I add two random numbers,
I expect their sum to be correct``()=
let x = randInt()
let y = randInt()
let expected = x + y
let actual = add(x,y)
Assert.AreEqual(expected,actual)
Let's use random numbers instead...
[<Test>]
let ``When I add two random numbers (100 times),
I expect their sum to be correct``()=
for _ in [1..100] do
let x = randInt()
let y = randInt()
let expected = x + y
let actual = add(x,y)
Assert.AreEqual(expected,actual)
Yea! Problem solved!
And why not do it 100 times just to be sure...
The EDFH can't beat this!
[<Test>]
let ``When I add two random numbers (100 times),
I expect their sum to be correct``()=
for _ in [1..100] do
let x = randInt()
let y = randInt()
let expected = x + y
let actual = add(x,y)
Assert.AreEqual(expected,actual)
Uh-oh!
But if you can't test by using +, how CAN you test?
We can't test "add" using +!
Question for everyone:
• How would you write a test for an "add"
function?
• But without re-implementing "add"
• And without using specific examples
Part II:
Property based testing
What are the "requirements" for
the "add" function?
Requirements for the "add" function?
• It's often hard to know where to get started
• Pro tip: compare it with something different...
– E.g. How does "add" differ from "subtract"
Requirements for the "add" function?
• Addition vs. subtraction:
– For subtraction, the order of the parameters
makes a difference
– For addition it doesn't.
[<Test>]
let ``When I add two numbers, the result
should not depend on parameter order``()=
for _ in [1..100] do
let x = randInt()
let y = randInt()
let result1 = add(x,y)
let result2 = add(y,x)
Assert.AreEqual(result1,result2)
reversed params
For subtraction, the order of the parameters makes a
difference, while for addition it doesn't.
let add(x,y) =
x * y
The EDFH responds with this implementation:
TEST: ``When I add two numbers, the result
should not depend on parameter order``
How about using the difference
between addition and multiplication?
For example:
*adding one twice is the same as adding two
*multiplying by one twice is NOT the same
as multiplying by two
[<Test>]
let ``Adding 1 twice is the same as adding 2``()=
for _ in [1..100] do
let x = randInt()
let result1 = add(add(x,1),1)
let result2 = add(x,2)
Assert.AreEqual(result1,result2)
Test: two "add 1"s is the same as one "add 2".
let add(x,y) =
x - y
The EDFH responds with:

TEST: ``When I add two numbers, the result
should not depend on parameter order``
TEST: ``Adding 1 twice is the same as adding 2``
Ha! Gotcha, EDFH!
But luckily we have the previous test as well!
let add(x,y) =
0
The EDFH responds with another implementation:

TEST: ``When I add two numbers, the result
should not depend on parameter order``
TEST: ``Adding 1 twice is the same as adding 2``

Aarrghh! Where did our approach go wrong?
Requirements for the "add" function
• We need to check that the result is somehow
connected to the input!
• Is there a trivial property of "add" that we
know the answer to?
– (without reimplementing our own version)
• Yes! Adding zero is the same as doing nothing
[<Test>]
let ``Adding zero is the same as doing nothing``()=
for _ in [1..100] do
let x = randInt()
let result1 = add(x,0)
let result2 = x
Assert.AreEqual(result1,result2)
Adding zero is the same as doing nothing
We have to check that the result is
somehow connected to the input.
Finally, the EDFH is defeated...

TEST: ``When I add two numbers, the result
should not depend on parameter order``
TEST: ``Adding 1 twice is the same as adding 2``

TEST: ``Adding zero is the same as doing nothing``

If these are all true we
MUST have a correct
implementation*
* not quite true
Refactoring
let propertyCheck propertyFn =
// property has type: (int,int) -> bool
for _ in [1..100] do
let x = randInt()
let y = randInt()
let result = propertyFn(x,y)
Assert.IsTrue(result)
Let's extract the shared code... Pass in a "property"
Check the property is
true for random inputs
let commutativeProperty(x,y) =
let result1 = add(x,y)
let result2 = add(y,x)
result1 = result2
And the tests now look like:
[<Test>]
let ``When I add two numbers, the result
should not depend on parameter order``()=
propertyCheck commutativeProperty
let adding1TwiceIsAdding2OnceProperty(x,_) =
let result1 = add(add(x,1),1)
let result2 = add(x,2)
result1 = result2
And the second property
[<Test>]
let ``Adding 1 twice is the same as adding 2``()=
propertyCheck adding1TwiceIsAdding2OnceProperty
let identityProperty(x,_) =
let result1 = add(x,0)
result1 = x
And the third property
[<Test>]
let ``Adding zero is the same as doing nothing``()=
propertyCheck identityProperty
Review
Testing with properties
• The parameter order doesn't matter
• Doing "add 1" twice is the same as
doing "add 2" once
• Adding zero does nothing
These properties
apply to ALL inputs
So we have a very
high confidence that
the implementation is
correct
Testing with properties
• "Commutativity" property
• "Associativity" property
• "Identity" property
These properties
define addition!
The EDFH can't create an
incorrect implementation!
Bonus: By using specifications, we have
understood the requirements in a deeper way.
Specification
Why bother with the EDFH?
Surely such a malicious programmer is
unrealistic and over-the-top?
Evil
Stupid
Lazy
In practice,
no difference!
In my career, I've always had to deal with one
stupid person in particular 
Me!
When I look at my old code, I almost always see something wrong!
I've often created flawed implementations, not out of evil
intent, but out of unawareness and blindness
The real EDFH!
Part III:
QuickCheck and its ilk
Wouldn't it be nice to have a toolkit for doing this?
The "QuickCheck" library was originally developed for Haskell by
Koen Claessen and John Hughes, and has been ported to many
other languages.
QuickCheck
Generator Shrinker
Your Property Function that returns bool
Checker API
Pass to checker
Generates
random inputs
Creates minimal
failing input
// correct implementation of add!
let add(x,y) = x + y
let commutativeProperty(x,y) =
let result1 = add(x,y)
let result2 = add(y,x)
result1 = result2
// check the property interactively
Check.Quick commutativeProperty
Using QuickCheck (FsCheck) looks like this:
Ok, passed 100 tests.
And get the output:
Generators:
making random inputs
QuickCheck
Generator Shrinker
Checker API
Generates ints
"int" generator 0, 1, 3, -2, ... etc
Generates strings
"string" generator "", "eiX$a^", "U%0Ika&r", ... etc
"bool" generator true, false, false, true, ... etc
Generating primitive types
Generates bools
Generates pairs of ints
"int*int" generator (0,0), (1,0), (2,0), (-1,1), (-1,2) ... etc
Generates options
"int option" generator Some 0, Some -1, None, Some -4; None ...
"Color" generator Green 47, Red, Blue true, Green -12, ...
Generating compound types
type Color = Red | Green of int | Blue of bool
Generates values of custom type
Define custom type
let commutativeProperty(x,y) =
let result1 = add(x,y)
let result2 = add(y,x) // reversed params
result1 = result2
(b) Appropriate generator will
be automatically created
int*int generator
(0,0) (1,0) (2,0) (-1,1) (100,-99) ...
(a) Checker detects that the
input is a pair of ints
Checker API
(c) Valid values will be generated...
(d) ...and passed to
the property for
evaluation
How it works in practice
Shrinking:
dealing with failure
QuickCheck
Generator Shrinker
Checker API
let smallerThan81Property x =
x < 81
Property to test – we know it's gonna fail!
"int" generator 0, 1, 3, -2, 34, -65, 100
Fails at 100!
So 100 fails, but knowing that is not very helpful
How shrinking works
Time to start shrinking!
let smallerThan81Property x =
x < 81
Shrink again starting at 88
How shrinking works
Shrink list for 100 0, 50, 75, 88, 94, 97, 99
Fails at 88!
Generate a new
sequence up to 100
Given a value, a shrinker produces a sequence of values
that are (in some way) smaller than the given value
let smallerThan81Property x =
x < 81
Shrink again starting at 83
How shrinking works
Shrink list for 88 0, 44, 66, 77, 83, 86, 87
Fails at 83!
Generate a new
sequence up to 88
Given a value, a shrinker produces a sequence of values
that are (in some way) smaller than the given value
let smallerThan81Property x =
x < 81
Shrink again starting at 81
How shrinking works
Shrink list for 83 0, 42, 63, 73, 78, 81, 82
Fails at 81!
Generate a new
sequence up to 83
Given a value, a shrinker produces a sequence of values
that are (in some way) smaller than the given value
let smallerThan81Property x =
x < 81
Shrink has determined that 81 is
the smallest failing input!
How shrinking works
Shrink list for 81 0, 41, 61, 71, 76, 79, 80
All pass!
Generate a new
sequence up to 81
Given a value, a shrinker produces a sequence of values
that are (in some way) smaller than the given value
Shrinking – final result
Check.Quick smallerThan81Property
// result: Falsifiable, after 23 tests (3 shrinks)
// 81
Shrinking is really helpful to show
the boundaries where errors happen
Shrinking is built into the check:
Part IV:
How to choose properties
ABC
123
do X do X
do Y
do Y
"Different paths, same destination"
Examples:
- Commutivity
- Associativity
- Map
- Monad & Functor laws
"Different paths, same destination"
Applied to a sort function
[1;2;3]
?
do ? do ?
List.sort
List.sort
"Different paths, same destination"
Applied to a sort function
[2;3;1]
[-2;-3;-1] [-3;-2;-1]
[1;2;3]
Negate
List.sort
List.sort
Negate
then reverse
"Different paths, same destination"
Applied to a sort function
[2;3;1]
[-2;-3;-1] [-3;-2;-1]
[1;2;3]
Negate
List.sort
List.sort
Negate
then reverse
"Different paths, same destination"
Applied to a map function
Currency(2)
.Map(x => x * 3)
Currency(2 * 3)
f x = x * 3
x
Option (x) Option (f x)
f x
Create
Map f
f
Create
Currency(x) Currency(f x)
"There and back again"
ABC 100101001
Do X
Inverse
Examples:
- Serialization/Deserialization
- Addition/Subtraction
-Write/Read
- SetProperty/GetProperty
"There and back again"
Applied to a list reverse function
[1;2;3] [3;2;1]
reverse
reverse
Pro tip:
We often need a combination of
properties, not just one
We needed three properties
to define "add"
"Some things never change"
 
transform
Examples:
- Size of a collection
- Contents of a collection
- Balanced trees
[2;3;1]
[-2;-3;-1] [-3;-2;-1]
[1;2;3]
Negate
List.sort
List.sort
Negate
then reverse
The EDFH and List.Sort
The EDFH can beat this!
The EDFH and List.Sort
[2;3;1]
[-2;-3;-1] [ ]
[ ]
Negate
List.evilSort
List.evilSort
Negate
then reverse
EvilSort just returns an empty list!
This passes the "commutivity" test!
"Some things never change"
[2;3;1]
[1; 2; 3]; [2; 1; 3]; [2; 3; 1];
[1; 3; 2]; [3; 1; 2]; [3; 2; 1]
[1;2;3]
List.sort
Must be one of these
permutations
Used to ensure the sort function is good
The EDFH is beaten now!
"The more things change,
the more they stay the same"
 
distinct

distinct
Idempotence:
- Sort
- Filter
- Event processing
- Required for distributed designs
"Solve a smaller problem first"
     
- Divide and conquer algorithms (e.g. quicksort)
- Structural induction (recursive data structures)
"Hard to prove, easy to verify"
- Prime number factorization
-Too many others to mention!
"Hard to prove, easy to verify"
Applied to a tokenizer
“a,b,c”
split
“a” “b” “c”
“a,b,c”
Combine and
verify
To verify the tokenizer, just check that the
concatenated tokens give us back the original string
"Hard to prove, easy to verify"
Applied to a sort
To verify the sort,
check that each pair is ordered
[2;3;1]
(1<=2) (2<=3)
[1;2;3]
List.sort
ABC
ABC 123
123
Compare
System
under test
Test Oracle
"The test oracle"
- Compare optimized with slow brute-force version
- Compare parallel with single thread version
- Legacy system is the oracle for a replacement system
PartV:
Model based testing
Using the test oracle approach
for complex implementations
Testing a simple database
Open Incr Close Incr Open Close
Open Decr Open
Four operations: Open, Close, Increment, Decrement
How do use this to check that our db works?
Let QuickCheck generate a random list of these actions for each client
Open Incr
Client
A
Client
B
Two clients: Client A and Client B
Testing a simple database
Compare model result with real system!
Open Incr Close Incr Open Close
Open Decr Open Open Incr
Test on real
system
Open Incr Close Incr Open Close
Open Decr Open Open Incr
Test on very
simple model1 00 0 1
(just an in-memory
accumulator)Connection closed,
so no change
Real world example:
Subtle bugs in an Erlang library
• The steps to reproduce were bizarre
– open-close-open file then exactly 3 ops in parallel
Real world Real world example:
Subtle bugs in an Erlang library
• The steps to reproduce were bizarre
– open-close-open file then exactly 3 ops in parallel
– no human would ever think to write this test case!
• Shrinker critical in finding a minimal sequence
• Great John Hughes talk and war stories
– NDC Oslo 2013 (https://vimeo.com/68383317)
Example-based tests vs.
Property-based tests
Example-based tests vs. Property-based tests
• PBTs are more general
– One property-based test can replace many example-
based tests.
• PBTs can reveal overlooked edge cases
– Nulls, negative numbers, weird strings, etc.
• PBTs ensure deep understanding of requirements
– Property-based tests force you to think! 
• Example-based tests are still helpful though!
– Less abstract, easier to understand
Summary
Be lazy! Don't write tests, generate them!
Use property-based thinking to gain
deeper insight into the requirements
PBT Resources
• Search for PBT and $YourLanguage
• Real world examples:
– John Hughes talks (vimeo.com/68383317)
– "Property-BasedTesting in a Screencast Editor" by
OskarWickström
– "MetamorphicTesting" by HillelWayne
The lazy programmer's guide to
writing 1000's of tests
An introduction to property based testing
Thanks!
@ScottWlaschin
fsharpforfunandprofit.com/pbt
Slides and video here
Contact me

More Related Content

What's hot

An introduction to property based testing
An introduction to property based testingAn introduction to property based testing
An introduction to property based testingScott Wlaschin
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented ProgrammingScott Wlaschin
 
Enterprise Tic-Tac-Toe
Enterprise Tic-Tac-ToeEnterprise Tic-Tac-Toe
Enterprise Tic-Tac-ToeScott Wlaschin
 
The Functional Programmer's Toolkit (NDC London 2019)
The Functional Programmer's Toolkit (NDC London 2019)The Functional Programmer's Toolkit (NDC London 2019)
The Functional Programmer's Toolkit (NDC London 2019)Scott Wlaschin
 
Quill vs Slick Smackdown
Quill vs Slick SmackdownQuill vs Slick Smackdown
Quill vs Slick SmackdownAlexander Ioffe
 
The Power Of Composition (DotNext 2019)
The Power Of Composition (DotNext 2019)The Power Of Composition (DotNext 2019)
The Power Of Composition (DotNext 2019)Scott Wlaschin
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Chris Richardson
 
Dr Frankenfunctor and the Monadster
Dr Frankenfunctor and the MonadsterDr Frankenfunctor and the Monadster
Dr Frankenfunctor and the MonadsterScott Wlaschin
 
Ad hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and CatsAd hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and CatsPhilip Schwarz
 
Functional Programming Patterns (BuildStuff '14)
Functional Programming Patterns (BuildStuff '14)Functional Programming Patterns (BuildStuff '14)
Functional Programming Patterns (BuildStuff '14)Scott Wlaschin
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...Philip Schwarz
 
Monoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsPhilip Schwarz
 
Clean code and Code Smells
Clean code and Code SmellsClean code and Code Smells
Clean code and Code SmellsMario Sangiorgio
 
Thirteen ways of looking at a turtle
Thirteen ways of looking at a turtleThirteen ways of looking at a turtle
Thirteen ways of looking at a turtleScott Wlaschin
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Mario Fusco
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1Philip Schwarz
 

What's hot (20)

An introduction to property based testing
An introduction to property based testingAn introduction to property based testing
An introduction to property based testing
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
 
Enterprise Tic-Tac-Toe
Enterprise Tic-Tac-ToeEnterprise Tic-Tac-Toe
Enterprise Tic-Tac-Toe
 
The Functional Programmer's Toolkit (NDC London 2019)
The Functional Programmer's Toolkit (NDC London 2019)The Functional Programmer's Toolkit (NDC London 2019)
The Functional Programmer's Toolkit (NDC London 2019)
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
 
Quill vs Slick Smackdown
Quill vs Slick SmackdownQuill vs Slick Smackdown
Quill vs Slick Smackdown
 
The Power Of Composition (DotNext 2019)
The Power Of Composition (DotNext 2019)The Power Of Composition (DotNext 2019)
The Power Of Composition (DotNext 2019)
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
 
Dr Frankenfunctor and the Monadster
Dr Frankenfunctor and the MonadsterDr Frankenfunctor and the Monadster
Dr Frankenfunctor and the Monadster
 
Ad hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and CatsAd hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and Cats
 
Clean code slide
Clean code slideClean code slide
Clean code slide
 
Functional Programming Patterns (BuildStuff '14)
Functional Programming Patterns (BuildStuff '14)Functional Programming Patterns (BuildStuff '14)
Functional Programming Patterns (BuildStuff '14)
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
Monoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and Cats
 
Clean code and Code Smells
Clean code and Code SmellsClean code and Code Smells
Clean code and Code Smells
 
Thirteen ways of looking at a turtle
Thirteen ways of looking at a turtleThirteen ways of looking at a turtle
Thirteen ways of looking at a turtle
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
 
Clean code
Clean codeClean code
Clean code
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
 
Clean code
Clean codeClean code
Clean code
 

Similar to The lazy programmer's guide to writing thousands of tests

Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven DevelopmentSheeju Alex
 
關於測試,我說的其實是......
關於測試,我說的其實是......關於測試,我說的其實是......
關於測試,我說的其實是......hugo lu
 
Functional Programming in Swift
Functional Programming in SwiftFunctional Programming in Swift
Functional Programming in SwiftSaugat Gautam
 
STAMP Descartes Presentation
STAMP Descartes PresentationSTAMP Descartes Presentation
STAMP Descartes PresentationSTAMP Project
 
An Introduction to Test Driven Development with React
An Introduction to Test Driven Development with ReactAn Introduction to Test Driven Development with React
An Introduction to Test Driven Development with ReactFITC
 
Tdd pecha kucha_v2
Tdd pecha kucha_v2Tdd pecha kucha_v2
Tdd pecha kucha_v2Paul Boos
 
Ruslan Shevchenko - Property based testing
Ruslan Shevchenko - Property based testingRuslan Shevchenko - Property based testing
Ruslan Shevchenko - Property based testingIevgenii Katsan
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with GroovyArturo Herrero
 
JVM Mechanics: Understanding the JIT's Tricks
JVM Mechanics: Understanding the JIT's TricksJVM Mechanics: Understanding the JIT's Tricks
JVM Mechanics: Understanding the JIT's TricksDoug Hawkins
 
Very basic functional design patterns
Very basic functional design patternsVery basic functional design patterns
Very basic functional design patternsTomasz Kowal
 
Testing in Python: doctest and unittest
Testing in Python: doctest and unittestTesting in Python: doctest and unittest
Testing in Python: doctest and unittestFariz Darari
 
Testing in Python: doctest and unittest (Updated)
Testing in Python: doctest and unittest (Updated)Testing in Python: doctest and unittest (Updated)
Testing in Python: doctest and unittest (Updated)Fariz Darari
 
MT_01_unittest_python.pdf
MT_01_unittest_python.pdfMT_01_unittest_python.pdf
MT_01_unittest_python.pdfHans Jones
 
Acceptance Testing With Selenium
Acceptance Testing With SeleniumAcceptance Testing With Selenium
Acceptance Testing With Seleniumelliando dias
 
Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test communityKerry Buckley
 
Mutation Testing with PIT
Mutation Testing with PITMutation Testing with PIT
Mutation Testing with PITRafał Leszko
 

Similar to The lazy programmer's guide to writing thousands of tests (20)

Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
關於測試,我說的其實是......
關於測試,我說的其實是......關於測試,我說的其實是......
關於測試,我說的其實是......
 
Functional Programming in Swift
Functional Programming in SwiftFunctional Programming in Swift
Functional Programming in Swift
 
STAMP Descartes Presentation
STAMP Descartes PresentationSTAMP Descartes Presentation
STAMP Descartes Presentation
 
ppopoff
ppopoffppopoff
ppopoff
 
An Introduction to Test Driven Development with React
An Introduction to Test Driven Development with ReactAn Introduction to Test Driven Development with React
An Introduction to Test Driven Development with React
 
Tdd pecha kucha_v2
Tdd pecha kucha_v2Tdd pecha kucha_v2
Tdd pecha kucha_v2
 
Ruslan Shevchenko - Property based testing
Ruslan Shevchenko - Property based testingRuslan Shevchenko - Property based testing
Ruslan Shevchenko - Property based testing
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
 
JVM Mechanics: Understanding the JIT's Tricks
JVM Mechanics: Understanding the JIT's TricksJVM Mechanics: Understanding the JIT's Tricks
JVM Mechanics: Understanding the JIT's Tricks
 
Property-based testing
Property-based testingProperty-based testing
Property-based testing
 
Very basic functional design patterns
Very basic functional design patternsVery basic functional design patterns
Very basic functional design patterns
 
Testing in Python: doctest and unittest
Testing in Python: doctest and unittestTesting in Python: doctest and unittest
Testing in Python: doctest and unittest
 
Testing in Python: doctest and unittest (Updated)
Testing in Python: doctest and unittest (Updated)Testing in Python: doctest and unittest (Updated)
Testing in Python: doctest and unittest (Updated)
 
MT_01_unittest_python.pdf
MT_01_unittest_python.pdfMT_01_unittest_python.pdf
MT_01_unittest_python.pdf
 
Introduction to python programming
Introduction to python programmingIntroduction to python programming
Introduction to python programming
 
Functional programming
Functional programmingFunctional programming
Functional programming
 
Acceptance Testing With Selenium
Acceptance Testing With SeleniumAcceptance Testing With Selenium
Acceptance Testing With Selenium
 
Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test community
 
Mutation Testing with PIT
Mutation Testing with PITMutation Testing with PIT
Mutation Testing with PIT
 

More from Scott Wlaschin

Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Scott Wlaschin
 
Building confidence in concurrent code with a model checker: TLA+ for program...
Building confidence in concurrent code with a model checker: TLA+ for program...Building confidence in concurrent code with a model checker: TLA+ for program...
Building confidence in concurrent code with a model checker: TLA+ for program...Scott Wlaschin
 
Reinventing the Transaction Script (NDC London 2020)
Reinventing the Transaction Script (NDC London 2020)Reinventing the Transaction Script (NDC London 2020)
Reinventing the Transaction Script (NDC London 2020)Scott Wlaschin
 
Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)Scott Wlaschin
 
Four Languages From Forty Years Ago (NewCrafts 2019)
Four Languages From Forty Years Ago (NewCrafts 2019)Four Languages From Forty Years Ago (NewCrafts 2019)
Four Languages From Forty Years Ago (NewCrafts 2019)Scott Wlaschin
 
Four Languages From Forty Years Ago
Four Languages From Forty Years AgoFour Languages From Forty Years Ago
Four Languages From Forty Years AgoScott Wlaschin
 
Designing with capabilities (DDD-EU 2017)
Designing with capabilities (DDD-EU 2017)Designing with capabilities (DDD-EU 2017)
Designing with capabilities (DDD-EU 2017)Scott Wlaschin
 
Designing with Capabilities
Designing with CapabilitiesDesigning with Capabilities
Designing with CapabilitiesScott Wlaschin
 
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Scott Wlaschin
 

More from Scott Wlaschin (13)

Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)
 
Building confidence in concurrent code with a model checker: TLA+ for program...
Building confidence in concurrent code with a model checker: TLA+ for program...Building confidence in concurrent code with a model checker: TLA+ for program...
Building confidence in concurrent code with a model checker: TLA+ for program...
 
Reinventing the Transaction Script (NDC London 2020)
Reinventing the Transaction Script (NDC London 2020)Reinventing the Transaction Script (NDC London 2020)
Reinventing the Transaction Script (NDC London 2020)
 
Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)
 
Four Languages From Forty Years Ago (NewCrafts 2019)
Four Languages From Forty Years Ago (NewCrafts 2019)Four Languages From Forty Years Ago (NewCrafts 2019)
Four Languages From Forty Years Ago (NewCrafts 2019)
 
Four Languages From Forty Years Ago
Four Languages From Forty Years AgoFour Languages From Forty Years Ago
Four Languages From Forty Years Ago
 
F# for C# Programmers
F# for C# ProgrammersF# for C# Programmers
F# for C# Programmers
 
Designing with capabilities (DDD-EU 2017)
Designing with capabilities (DDD-EU 2017)Designing with capabilities (DDD-EU 2017)
Designing with capabilities (DDD-EU 2017)
 
Designing with Capabilities
Designing with CapabilitiesDesigning with Capabilities
Designing with Capabilities
 
Swift vs. Language X
Swift vs. Language XSwift vs. Language X
Swift vs. Language X
 
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
 
Doge-driven design
Doge-driven designDoge-driven design
Doge-driven design
 
The Theory of Chains
The Theory of ChainsThe Theory of Chains
The Theory of Chains
 

Recently uploaded

AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplatePresentation.STUDIO
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...kalichargn70th171
 
How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...software pro Development
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfryanfarris8
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfproinshot.com
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech studentsHimanshiGarg82
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024Mind IT Systems
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdfPearlKirahMaeRagusta1
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionOnePlan Solutions
 

Recently uploaded (20)

CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
 
How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 

The lazy programmer's guide to writing thousands of tests

  • 1. The lazy programmer's guide to writing 1000's of tests An introduction to property based testing @ScottWlaschin fsharpforfunandprofit.com
  • 2. The NDC community right now
  • 3. Part 1: In which I have a conversation with a remote developer This was a project from a long time ago, in a galaxy far far away
  • 4. For some reason we needed a custom "add" function
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17. Seriously, how *do* you know that you have enough tests?
  • 18. So I decide to start writing the unit tests myself
  • 19. [<Test>] let ``When I add 1 + 3, I expect 4``()= let result = add(1,3) Assert.AreEqual(4,result) [<Test>] let ``When I add 2 + 2, I expect 4``()= let result = add(2,2) Assert.AreEqual(4,result)   First, I had a look at the existing tests...
  • 20. [<Test>] let ``When I add -1 + 3, I expect 2``()= let result = add(-1,3) Assert.AreEqual(2,result)  Ok, now for my first new test...
  • 21. let add(x,y) = 4 wtf! Hmm.. let's look at the implementation...
  • 22.
  • 23.
  • 24. [<Test>] let ``When I add 2 + 3, I expect 5``()= let result = add(2,3) Assert.AreEqual(5,result) [<Test>] let ``When I add 1 + 41, I expect 42``()= let result = add(1,41) Assert.AreEqual(42,result)   Time for some more tests...
  • 25. let add(x,y) = match (x,y) with | (2,3) -> 5 | (1,41) -> 42 | (_,_) -> 4 // all other cases But let's just check the implementation again...
  • 26.
  • 27.
  • 28. Write only enough code to make the failing unit test pass. http://www.javiersaldana.com/articles/tech/refactoring-the-three-laws-of-tdd TDD best practices
  • 29. [<Test>] let ``When I add two numbers, I expect to get their sum``()= let testData = [ (1,2,3) (2,2,4) (3,5,8) (27,15,42) ] for (x,y,expected) in testData do let actual = add(x,y) Assert.AreEqual(expected,actual) Another attempt at a test 
  • 30. let add(x,y) = match (x,y) with | (1,2) -> 3 | (2,3) -> 5 | (3,5) -> 8 | (1,41) -> 42 | (27,15) -> 42 | (_,_) -> 4 // all other cases Let's check the implementation one more time....
  • 31. It dawned on me who I was dealing with... ...the legendary burned-out, always lazy and often malicious programmer called...
  • 33. Rethinking the approach The EDFH will always make specific examples pass, no matter what I do... So let's not use specific examples!
  • 34. [<Test>] let ``When I add two random numbers, I expect their sum to be correct``()= let x = randInt() let y = randInt() let expected = x + y let actual = add(x,y) Assert.AreEqual(expected,actual) Let's use random numbers instead...
  • 35. [<Test>] let ``When I add two random numbers (100 times), I expect their sum to be correct``()= for _ in [1..100] do let x = randInt() let y = randInt() let expected = x + y let actual = add(x,y) Assert.AreEqual(expected,actual) Yea! Problem solved! And why not do it 100 times just to be sure... The EDFH can't beat this!
  • 36. [<Test>] let ``When I add two random numbers (100 times), I expect their sum to be correct``()= for _ in [1..100] do let x = randInt() let y = randInt() let expected = x + y let actual = add(x,y) Assert.AreEqual(expected,actual) Uh-oh! But if you can't test by using +, how CAN you test? We can't test "add" using +!
  • 37. Question for everyone: • How would you write a test for an "add" function? • But without re-implementing "add" • And without using specific examples
  • 39. What are the "requirements" for the "add" function?
  • 40. Requirements for the "add" function? • It's often hard to know where to get started • Pro tip: compare it with something different... – E.g. How does "add" differ from "subtract"
  • 41. Requirements for the "add" function? • Addition vs. subtraction: – For subtraction, the order of the parameters makes a difference – For addition it doesn't.
  • 42. [<Test>] let ``When I add two numbers, the result should not depend on parameter order``()= for _ in [1..100] do let x = randInt() let y = randInt() let result1 = add(x,y) let result2 = add(y,x) Assert.AreEqual(result1,result2) reversed params For subtraction, the order of the parameters makes a difference, while for addition it doesn't.
  • 43. let add(x,y) = x * y The EDFH responds with this implementation: TEST: ``When I add two numbers, the result should not depend on parameter order``
  • 44. How about using the difference between addition and multiplication? For example: *adding one twice is the same as adding two *multiplying by one twice is NOT the same as multiplying by two
  • 45. [<Test>] let ``Adding 1 twice is the same as adding 2``()= for _ in [1..100] do let x = randInt() let result1 = add(add(x,1),1) let result2 = add(x,2) Assert.AreEqual(result1,result2) Test: two "add 1"s is the same as one "add 2".
  • 46. let add(x,y) = x - y The EDFH responds with:  TEST: ``When I add two numbers, the result should not depend on parameter order`` TEST: ``Adding 1 twice is the same as adding 2`` Ha! Gotcha, EDFH! But luckily we have the previous test as well!
  • 47. let add(x,y) = 0 The EDFH responds with another implementation:  TEST: ``When I add two numbers, the result should not depend on parameter order`` TEST: ``Adding 1 twice is the same as adding 2``  Aarrghh! Where did our approach go wrong?
  • 48. Requirements for the "add" function • We need to check that the result is somehow connected to the input! • Is there a trivial property of "add" that we know the answer to? – (without reimplementing our own version) • Yes! Adding zero is the same as doing nothing
  • 49. [<Test>] let ``Adding zero is the same as doing nothing``()= for _ in [1..100] do let x = randInt() let result1 = add(x,0) let result2 = x Assert.AreEqual(result1,result2) Adding zero is the same as doing nothing We have to check that the result is somehow connected to the input.
  • 50. Finally, the EDFH is defeated...  TEST: ``When I add two numbers, the result should not depend on parameter order`` TEST: ``Adding 1 twice is the same as adding 2``  TEST: ``Adding zero is the same as doing nothing``  If these are all true we MUST have a correct implementation* * not quite true
  • 52. let propertyCheck propertyFn = // property has type: (int,int) -> bool for _ in [1..100] do let x = randInt() let y = randInt() let result = propertyFn(x,y) Assert.IsTrue(result) Let's extract the shared code... Pass in a "property" Check the property is true for random inputs
  • 53. let commutativeProperty(x,y) = let result1 = add(x,y) let result2 = add(y,x) result1 = result2 And the tests now look like: [<Test>] let ``When I add two numbers, the result should not depend on parameter order``()= propertyCheck commutativeProperty
  • 54. let adding1TwiceIsAdding2OnceProperty(x,_) = let result1 = add(add(x,1),1) let result2 = add(x,2) result1 = result2 And the second property [<Test>] let ``Adding 1 twice is the same as adding 2``()= propertyCheck adding1TwiceIsAdding2OnceProperty
  • 55. let identityProperty(x,_) = let result1 = add(x,0) result1 = x And the third property [<Test>] let ``Adding zero is the same as doing nothing``()= propertyCheck identityProperty
  • 57. Testing with properties • The parameter order doesn't matter • Doing "add 1" twice is the same as doing "add 2" once • Adding zero does nothing These properties apply to ALL inputs So we have a very high confidence that the implementation is correct
  • 58. Testing with properties • "Commutativity" property • "Associativity" property • "Identity" property These properties define addition! The EDFH can't create an incorrect implementation! Bonus: By using specifications, we have understood the requirements in a deeper way. Specification
  • 59. Why bother with the EDFH? Surely such a malicious programmer is unrealistic and over-the-top?
  • 61. In my career, I've always had to deal with one stupid person in particular  Me! When I look at my old code, I almost always see something wrong! I've often created flawed implementations, not out of evil intent, but out of unawareness and blindness The real EDFH!
  • 62. Part III: QuickCheck and its ilk Wouldn't it be nice to have a toolkit for doing this? The "QuickCheck" library was originally developed for Haskell by Koen Claessen and John Hughes, and has been ported to many other languages.
  • 63. QuickCheck Generator Shrinker Your Property Function that returns bool Checker API Pass to checker Generates random inputs Creates minimal failing input
  • 64. // correct implementation of add! let add(x,y) = x + y let commutativeProperty(x,y) = let result1 = add(x,y) let result2 = add(y,x) result1 = result2 // check the property interactively Check.Quick commutativeProperty Using QuickCheck (FsCheck) looks like this: Ok, passed 100 tests. And get the output:
  • 66. Generates ints "int" generator 0, 1, 3, -2, ... etc Generates strings "string" generator "", "eiX$a^", "U%0Ika&r", ... etc "bool" generator true, false, false, true, ... etc Generating primitive types Generates bools
  • 67. Generates pairs of ints "int*int" generator (0,0), (1,0), (2,0), (-1,1), (-1,2) ... etc Generates options "int option" generator Some 0, Some -1, None, Some -4; None ... "Color" generator Green 47, Red, Blue true, Green -12, ... Generating compound types type Color = Red | Green of int | Blue of bool Generates values of custom type Define custom type
  • 68. let commutativeProperty(x,y) = let result1 = add(x,y) let result2 = add(y,x) // reversed params result1 = result2 (b) Appropriate generator will be automatically created int*int generator (0,0) (1,0) (2,0) (-1,1) (100,-99) ... (a) Checker detects that the input is a pair of ints Checker API (c) Valid values will be generated... (d) ...and passed to the property for evaluation How it works in practice
  • 70. let smallerThan81Property x = x < 81 Property to test – we know it's gonna fail! "int" generator 0, 1, 3, -2, 34, -65, 100 Fails at 100! So 100 fails, but knowing that is not very helpful How shrinking works Time to start shrinking!
  • 71. let smallerThan81Property x = x < 81 Shrink again starting at 88 How shrinking works Shrink list for 100 0, 50, 75, 88, 94, 97, 99 Fails at 88! Generate a new sequence up to 100 Given a value, a shrinker produces a sequence of values that are (in some way) smaller than the given value
  • 72. let smallerThan81Property x = x < 81 Shrink again starting at 83 How shrinking works Shrink list for 88 0, 44, 66, 77, 83, 86, 87 Fails at 83! Generate a new sequence up to 88 Given a value, a shrinker produces a sequence of values that are (in some way) smaller than the given value
  • 73. let smallerThan81Property x = x < 81 Shrink again starting at 81 How shrinking works Shrink list for 83 0, 42, 63, 73, 78, 81, 82 Fails at 81! Generate a new sequence up to 83 Given a value, a shrinker produces a sequence of values that are (in some way) smaller than the given value
  • 74. let smallerThan81Property x = x < 81 Shrink has determined that 81 is the smallest failing input! How shrinking works Shrink list for 81 0, 41, 61, 71, 76, 79, 80 All pass! Generate a new sequence up to 81 Given a value, a shrinker produces a sequence of values that are (in some way) smaller than the given value
  • 75. Shrinking – final result Check.Quick smallerThan81Property // result: Falsifiable, after 23 tests (3 shrinks) // 81 Shrinking is really helpful to show the boundaries where errors happen Shrinking is built into the check:
  • 76. Part IV: How to choose properties
  • 77. ABC 123 do X do X do Y do Y "Different paths, same destination" Examples: - Commutivity - Associativity - Map - Monad & Functor laws
  • 78. "Different paths, same destination" Applied to a sort function [1;2;3] ? do ? do ? List.sort List.sort
  • 79. "Different paths, same destination" Applied to a sort function [2;3;1] [-2;-3;-1] [-3;-2;-1] [1;2;3] Negate List.sort List.sort Negate then reverse
  • 80. "Different paths, same destination" Applied to a sort function [2;3;1] [-2;-3;-1] [-3;-2;-1] [1;2;3] Negate List.sort List.sort Negate then reverse
  • 81. "Different paths, same destination" Applied to a map function Currency(2) .Map(x => x * 3) Currency(2 * 3) f x = x * 3 x Option (x) Option (f x) f x Create Map f f Create Currency(x) Currency(f x)
  • 82. "There and back again" ABC 100101001 Do X Inverse Examples: - Serialization/Deserialization - Addition/Subtraction -Write/Read - SetProperty/GetProperty
  • 83. "There and back again" Applied to a list reverse function [1;2;3] [3;2;1] reverse reverse
  • 84. Pro tip: We often need a combination of properties, not just one We needed three properties to define "add"
  • 85. "Some things never change"   transform Examples: - Size of a collection - Contents of a collection - Balanced trees
  • 87. The EDFH and List.Sort [2;3;1] [-2;-3;-1] [ ] [ ] Negate List.evilSort List.evilSort Negate then reverse EvilSort just returns an empty list! This passes the "commutivity" test!
  • 88. "Some things never change" [2;3;1] [1; 2; 3]; [2; 1; 3]; [2; 3; 1]; [1; 3; 2]; [3; 1; 2]; [3; 2; 1] [1;2;3] List.sort Must be one of these permutations Used to ensure the sort function is good The EDFH is beaten now!
  • 89. "The more things change, the more they stay the same"   distinct  distinct Idempotence: - Sort - Filter - Event processing - Required for distributed designs
  • 90. "Solve a smaller problem first"       - Divide and conquer algorithms (e.g. quicksort) - Structural induction (recursive data structures)
  • 91. "Hard to prove, easy to verify" - Prime number factorization -Too many others to mention!
  • 92. "Hard to prove, easy to verify" Applied to a tokenizer “a,b,c” split “a” “b” “c” “a,b,c” Combine and verify To verify the tokenizer, just check that the concatenated tokens give us back the original string
  • 93. "Hard to prove, easy to verify" Applied to a sort To verify the sort, check that each pair is ordered [2;3;1] (1<=2) (2<=3) [1;2;3] List.sort
  • 94. ABC ABC 123 123 Compare System under test Test Oracle "The test oracle" - Compare optimized with slow brute-force version - Compare parallel with single thread version - Legacy system is the oracle for a replacement system
  • 95. PartV: Model based testing Using the test oracle approach for complex implementations
  • 96. Testing a simple database Open Incr Close Incr Open Close Open Decr Open Four operations: Open, Close, Increment, Decrement How do use this to check that our db works? Let QuickCheck generate a random list of these actions for each client Open Incr Client A Client B Two clients: Client A and Client B
  • 97. Testing a simple database Compare model result with real system! Open Incr Close Incr Open Close Open Decr Open Open Incr Test on real system Open Incr Close Incr Open Close Open Decr Open Open Incr Test on very simple model1 00 0 1 (just an in-memory accumulator)Connection closed, so no change
  • 98. Real world example: Subtle bugs in an Erlang library • The steps to reproduce were bizarre – open-close-open file then exactly 3 ops in parallel
  • 99.
  • 100. Real world Real world example: Subtle bugs in an Erlang library • The steps to reproduce were bizarre – open-close-open file then exactly 3 ops in parallel – no human would ever think to write this test case! • Shrinker critical in finding a minimal sequence • Great John Hughes talk and war stories – NDC Oslo 2013 (https://vimeo.com/68383317)
  • 102. Example-based tests vs. Property-based tests • PBTs are more general – One property-based test can replace many example- based tests. • PBTs can reveal overlooked edge cases – Nulls, negative numbers, weird strings, etc. • PBTs ensure deep understanding of requirements – Property-based tests force you to think!  • Example-based tests are still helpful though! – Less abstract, easier to understand
  • 103. Summary Be lazy! Don't write tests, generate them! Use property-based thinking to gain deeper insight into the requirements
  • 104. PBT Resources • Search for PBT and $YourLanguage • Real world examples: – John Hughes talks (vimeo.com/68383317) – "Property-BasedTesting in a Screencast Editor" by OskarWickström – "MetamorphicTesting" by HillelWayne
  • 105. The lazy programmer's guide to writing 1000's of tests An introduction to property based testing Thanks! @ScottWlaschin fsharpforfunandprofit.com/pbt Slides and video here Contact me