SlideShare a Scribd company logo
QCon London 2016
An Introduction to Property Based Testing
Aaron Bedra
Chief Security Officer, Eligible
@abedra
InfoQ.com: News & Community Site
• 750,000 unique visitors/month
• Published in 4 languages (English, Chinese, Japanese and Brazilian
Portuguese)
• Post content from our QCon conferences
• News 15-20 / week
• Articles 3-4 / week
• Presentations (videos) 12-15 / week
• Interviews 2-3 / week
• Books 1 / month
Watch the video with slide
synchronization on InfoQ.com!
http://www.infoq.com/presentations
/property-based-testing-2016
Purpose of QCon
- to empower software development by facilitating the spread of
knowledge and innovation
Strategy
- practitioner-driven conference designed for YOU: influencers of
change and innovation in your teams
- speakers and topics driving the evolution and innovation
- connecting and catalyzing the influencers and innovators
Highlights
- attended by more than 12,000 delegates since 2007
- held in 9 cities worldwide
Presented at QCon London
www.qconlondon.com
Why do we test?
• To better understand what we are building
• To help us think deeper about what we are building
• To ensure the correctness of what we are building
• To help us explore our design*
• To explain to others how our code should work
How do we test?
• With compilers (type systems, static analysis, etc)
• Manual testing
• X-Unit style tests
• Property/generative based tests
• Formal modeling
How do we test?
• With compilers (type systems, static analysis, etc)
• Manual testing
• X-Unit style tests
• Property/generative based tests
• Formal modeling
What is it?
An abstraction
Property based testing eliminates
the guess work on value and
order of operations testing
Magic numbers
Instead of specifying
how you specify what
Testing over time
When we start our test
suite, things are usually
easy to understand
public class Basic {
public static Integer calculate(Integer x, Integer y) {
return x + y;
}
}
public class BasicTest {
@Test
public void TestCalculate() {
assertEquals(Integer.valueOf(5), Basic.calculate(3, 2));
}
}
What other tests might
we write for this code?
Like all programs we
start simple
But over time things get
more complicated
What happens when our simple
calculate function grows to
include an entire domain?
Our test suite will undoubtedly
grow, but we have options to
control the growth
And also maintain
confidence in our tests
An Introduction to Property Based Testing
By changing our mental
model just a bit we can
cover much more ground
Let’s revisit our basic
example
public class Basic {
public static Integer calculate(Integer x, Integer y) {
return x + y;
}
}
But instead of a unit test,
let’s write a property
@RunWith(JUnitQuickcheck.class)
public class BasicProperties {
@Property public void calculateBaseAssumption(Integer x, Integer y) {
Integer expected = x + y;
assertEquals(expected, Basic.calculate(x, y));
}
}
public class BasicTest {
@Test
public void TestCalculate() {
assertEquals(Integer.valueOf(5), Basic.calculate(3, 2));
}
}
@RunWith(JUnitQuickcheck.class)
public class BasicProperties {
@Property(trials = 1000000) public void
calculateBaseAssumption(Integer x, Integer y) {
Integer expected = x + y;
assertEquals(expected, Basic.calculate(x, y));
}
}
This property isn’t much
different than the unit test
we had before it
It’s just one level of
abstraction higher
Let’s add a constraint to
our calculator
Let’s say that the output
cannot be negative
public class Basic {
public static Integer calculate(Integer x, Integer y) {
Integer total = x + y;
if (total < 0) {
return 0;
} else {
return total;
}
}
}
java.lang.AssertionError: Property calculateBaseAssumption falsified for args
shrunken to [0, -679447654]
Shrinking
@RunWith(JUnitQuickcheck.class)
public class BasicProperties {
@Property public void calculateBaseAssumption(Integer x, Integer y) {
Integer expected = x + y;
assertEquals(expected, Basic.calculate(x, y));
}
}
public class Basic {
public static Integer calculate(Integer x, Integer y) {
Integer total = x + y;
if (total < 0) {
return 0;
} else {
return total;
}
}
}
Now we can be more
specific with our property
@RunWith(JUnitQuickcheck.class)
public class BasicProperties {
@Property public void calculateBaseAssumption(Integer x, Integer y) {
assumeThat(x, greaterThan(0));
assumeThat(y, greaterThan(0));
assertThat(Basic.calculate(x, y), is(greaterThan(0)));
}
}
java.lang.AssertionError: Property calculateBaseAssumption falsified for args shrunken to [647853159,
1499681379]
We could keep going from
here but let’s dive into
some of the concepts
Refactoring
This is one of my favorite
use cases for invoking
property based testing
Legacy code becomes
the model
It’s incredibly powerful
It ensures you have
exact feature parity
Even for unintended
features!
Generators
You can use them for all
kinds of things
Scenario
Every route in your web
application
You could define
generators based on your
routes
And create valid and
invalid inputs for every
endpoint
You could run the
generators on every test
Or save the output of the
generation for faster
execution
Saved execution of
generators can even bring
you to simulation testing
There are tons of property
based testing libraries
available
But this is a talk in a
functional language track
So let’s have some fun
Let’s pretend we have
some legacy code
Written in C
And we want to test it to
make sure it actually
works
But there are no
quickcheck libraries
available*
Warning! The crypto you are
about to see should not be
attempted at work
Caesar’s Cipher
Let’s start with our
implementation
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *caesar(int shift, char *input)
{
char *output = malloc(strlen(input));
memset(output, '0', strlen(input));
for (int x = 0; x < strlen(input); x++) {
if (isalpha(input[x])) {
int c = toupper(input[x]);
c = (((c - 65) + shift) % 26) + 65;
output[x] = c;
} else {
output[x] = input[x];
}
}
return output;
}
Next we create a new
implementation to test
against
caesar :: Int -> String -> String
caesar k = map f
where
f c
| inRange ('A', 'Z') c = chr $ ord 'A' +
(ord c - ord 'A' + k) `mod` 26
| otherwise = c
We now have two
functions that “should” do
the same thing
But they aren’t in the
same language
Thankfully Haskell has
good FFI support
foreign import ccall "caesar.h caesar"
c_caesar :: CInt -> CString -> CString
native_caesar :: Int -> String -> IO String
native_caesar shift input = withCString input $ c_str ->
peekCString(c_caesar (fromIntegral shift) c_str)
$ stack exec ghci caesar.hs caesar.so
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
[1 of 1] Compiling Main ( caesar.hs, interpreted )
Ok, modules loaded: Main.
*Main> caesar 2 "ATTACKATDAWN"
"CVVCEMCVFCYP"
*Main> native_caesar 2 "ATTACKATDAWN"
"CVVCEMCVFCYP"
We can now execute our
C code from inside of
Haskell
We can use Haskell’s
quickcheck library to
verify our C code
First we need to write a
property
unsafeEq :: IO String -> String -> Bool
unsafeEq x y = unsafePerformIO(x) == y
genSafeChar :: Gen Char
genSafeChar = elements ['A' .. 'Z']
genSafeString :: Gen String
genSafeString = listOf genSafeChar
newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show
instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString
equivalenceProperty = forAll genSafeString $ str ->
unsafeEq (native_caesar 2 str) (caesar 2 str)
unsafeEq :: IO String -> String -> Bool
unsafeEq x y = unsafePerformIO(x) == y
genSafeChar :: Gen Char
genSafeChar = elements ['A' .. 'Z']
genSafeString :: Gen String
genSafeString = listOf genSafeChar
newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show
instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString
equivalenceProperty = forAll genSafeString $ str ->
unsafeEq (native_caesar 2 str) (caesar 2 str)
unsafeEq :: IO String -> String -> Bool
unsafeEq x y = unsafePerformIO(x) == y
genSafeChar :: Gen Char
genSafeChar = elements ['A' .. 'Z']
genSafeString :: Gen String
genSafeString = listOf genSafeChar
newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show
instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString
equivalenceProperty = forAll genSafeString $ str ->
unsafeEq (native_caesar 2 str) (caesar 2 str)
unsafeEq :: IO String -> String -> Bool
unsafeEq x y = unsafePerformIO(x) == y
genSafeChar :: Gen Char
genSafeChar = elements ['A' .. 'Z']
genSafeString :: Gen String
genSafeString = listOf genSafeChar
newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show
instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString
equivalenceProperty = forAll genSafeString $ str ->
unsafeEq (native_caesar 2 str) (caesar 2 str)
unsafeEq :: IO String -> String -> Bool
unsafeEq x y = unsafePerformIO(x) == y
genSafeChar :: Gen Char
genSafeChar = elements ['A' .. 'Z']
genSafeString :: Gen String
genSafeString = listOf genSafeChar
newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show
instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString
equivalenceProperty = forAll genSafeString $ str ->
unsafeEq (native_caesar 2 str) (caesar 2 str)
unsafeEq :: IO String -> String -> Bool
unsafeEq x y = unsafePerformIO(x) == y
genSafeChar :: Gen Char
genSafeChar = elements ['A' .. 'Z']
genSafeString :: Gen String
genSafeString = listOf genSafeChar
newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show
instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString
equivalenceProperty = forAll genSafeString $ str ->
unsafeEq (native_caesar 2 str) (caesar 2 str)
*Main> quickCheck equivalenceProperty
*** Failed! Falsifiable (after 20 tests):
"QYMSMCWTIXNDFDMLSL"
*Main> caesar 2 "QYMSMCWTIXNDFDMLSL"
"SAOUOEYVKZPFHFONUN"
*Main> native_caesar 2 "QYMSMCWTIXNDFDMLSL"
“SAOUOEYVKZPFHFONUN/Users/abedra/x“
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *caesar(int shift, char *input)
{
char *output = malloc(strlen(input));
memset(output, '0', strlen(input));
for (int x = 0; x < strlen(input); x++) {
if (isalpha(input[x])) {
int c = toupper(input[x]);
c = (((c - 65) + shift) % 26) + 65;
output[x] = c;
} else {
output[x] = input[x];
}
}
return output;
}
We’ve found a memory
handling issue in our C
code!
In reality there are more
issues with this code, but our
issue was quickly exposed
And easily reproduced
Wrapping up
Not all testing is created
equal
You should use as many
different testing
techniques as you need
Remember to think about
the limits of your tools
And use tools that help
you achieve your results
more effectively
And more efficiently
Questions?
Watch the video with slide synchronization on
InfoQ.com!
http://www.infoq.com/presentations/property-
based-testing-2016

More Related Content

What's hot (20)

PDF
Zio from Home
Wiem Zine Elabidine
 
PPTX
Flying Futures at the same sky can make the sun rise at midnight
Wiem Zine Elabidine
 
PDF
Functions, Types, Programs and Effects
Raymond Roestenburg
 
PDF
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Mario Fusco
 
PPTX
Category theory, Monads, and Duality in the world of (BIG) Data
greenwop
 
PDF
Beyond xUnit example-based testing: property-based testing with ScalaCheck
Franklin Chen
 
PDF
Fiber supervision in ZIO
Wiem Zine Elabidine
 
PPTX
07. Arrays
Intro C# Book
 
PDF
Learning Functional Programming Without Growing a Neckbeard
Kelsey Gilmore-Innis
 
PDF
Twins: Object Oriented Programming and Functional Programming
RichardWarburton
 
PPTX
ZIO: Powerful and Principled Functional Programming in Scala
Wiem Zine Elabidine
 
PDF
Berlin meetup
Wiem Zine Elabidine
 
PDF
ZIO Queue
Wiem Zine Elabidine
 
PDF
Dr Frankenfunctor and the Monadster
Scott Wlaschin
 
PPTX
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Mario Fusco
 
PDF
Functional Design Patterns (DevTernity 2018)
Scott Wlaschin
 
PDF
Friendly Functional Programming
Wiem Zine Elabidine
 
PDF
Akka tips
Raymond Roestenburg
 
PDF
Java Simple Programs
Upender Upr
 
PDF
An Introduction to Functional Programming - DeveloperUG - 20140311
Andreas Pauley
 
Zio from Home
Wiem Zine Elabidine
 
Flying Futures at the same sky can make the sun rise at midnight
Wiem Zine Elabidine
 
Functions, Types, Programs and Effects
Raymond Roestenburg
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Mario Fusco
 
Category theory, Monads, and Duality in the world of (BIG) Data
greenwop
 
Beyond xUnit example-based testing: property-based testing with ScalaCheck
Franklin Chen
 
Fiber supervision in ZIO
Wiem Zine Elabidine
 
07. Arrays
Intro C# Book
 
Learning Functional Programming Without Growing a Neckbeard
Kelsey Gilmore-Innis
 
Twins: Object Oriented Programming and Functional Programming
RichardWarburton
 
ZIO: Powerful and Principled Functional Programming in Scala
Wiem Zine Elabidine
 
Berlin meetup
Wiem Zine Elabidine
 
Dr Frankenfunctor and the Monadster
Scott Wlaschin
 
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Mario Fusco
 
Functional Design Patterns (DevTernity 2018)
Scott Wlaschin
 
Friendly Functional Programming
Wiem Zine Elabidine
 
Java Simple Programs
Upender Upr
 
An Introduction to Functional Programming - DeveloperUG - 20140311
Andreas Pauley
 

Viewers also liked (6)

PPTX
Advances in Unit Testing: Theory and Practice
Tao Xie
 
PPTX
Dealing with combinatorial explosions and boring tests
Alexander Tarlinder
 
PDF
Testing at Yammer with FooUnit, Jellyfish, and Sauce Labs
Sauce Labs
 
PDF
JUnit Kung Fu: Getting More Out of Your Unit Tests
John Ferguson Smart Limited
 
PDF
Property based Testing - generative data & executable domain rules
Debasish Ghosh
 
PDF
Down the Rabbit Hole
Charles Nutter
 
Advances in Unit Testing: Theory and Practice
Tao Xie
 
Dealing with combinatorial explosions and boring tests
Alexander Tarlinder
 
Testing at Yammer with FooUnit, Jellyfish, and Sauce Labs
Sauce Labs
 
JUnit Kung Fu: Getting More Out of Your Unit Tests
John Ferguson Smart Limited
 
Property based Testing - generative data & executable domain rules
Debasish Ghosh
 
Down the Rabbit Hole
Charles Nutter
 
Ad

Similar to An Introduction to Property Based Testing (20)

PDF
Aaron Bedra - Effective Software Security Teams
centralohioissa
 
PDF
A new execution model for Nashorn in Java 9
Marcus Lagergren
 
PPTX
Getting started with ES6
Nitay Neeman
 
PPSX
Javascript variables and datatypes
Varun C M
 
PDF
ppopoff
Paul Popoff
 
PDF
L5, Loop and iteration, CSE 202, BN11.pdf
SauravBarua11
 
PDF
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Robot Media
 
ODP
Bring the fun back to java
ciklum_ods
 
PDF
JavaScript(Es5) Interview Questions & Answers
Ratnala Charan kumar
 
PPT
Expert JavaScript tricks of the masters
Ara Pehlivanian
 
PPTX
Event-driven IO server-side JavaScript environment based on V8 Engine
Ricardo Silva
 
PDF
Durable functions 2.0 (2019-10-10)
Paco de la Cruz
 
PDF
JJUG CCC 2011 Spring
Kiyotaka Oku
 
PPT
JavaScript Misunderstood
Bhavya Siddappa
 
KEY
CouchDB on Android
Sven Haiges
 
PDF
Cocoa heads 09112017
Vincent Pradeilles
 
PPTX
ES6 Overview
Bruno Scopelliti
 
PDF
MT_01_unittest_python.pdf
Hans Jones
 
PPTX
Intro to Javascript
Anjan Banda
 
Aaron Bedra - Effective Software Security Teams
centralohioissa
 
A new execution model for Nashorn in Java 9
Marcus Lagergren
 
Getting started with ES6
Nitay Neeman
 
Javascript variables and datatypes
Varun C M
 
ppopoff
Paul Popoff
 
L5, Loop and iteration, CSE 202, BN11.pdf
SauravBarua11
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Robot Media
 
Bring the fun back to java
ciklum_ods
 
JavaScript(Es5) Interview Questions & Answers
Ratnala Charan kumar
 
Expert JavaScript tricks of the masters
Ara Pehlivanian
 
Event-driven IO server-side JavaScript environment based on V8 Engine
Ricardo Silva
 
Durable functions 2.0 (2019-10-10)
Paco de la Cruz
 
JJUG CCC 2011 Spring
Kiyotaka Oku
 
JavaScript Misunderstood
Bhavya Siddappa
 
CouchDB on Android
Sven Haiges
 
Cocoa heads 09112017
Vincent Pradeilles
 
ES6 Overview
Bruno Scopelliti
 
MT_01_unittest_python.pdf
Hans Jones
 
Intro to Javascript
Anjan Banda
 
Ad

More from C4Media (20)

PDF
Streaming a Million Likes/Second: Real-Time Interactions on Live Video
C4Media
 
PDF
Next Generation Client APIs in Envoy Mobile
C4Media
 
PDF
Software Teams and Teamwork Trends Report Q1 2020
C4Media
 
PDF
Understand the Trade-offs Using Compilers for Java Applications
C4Media
 
PDF
Kafka Needs No Keeper
C4Media
 
PDF
High Performing Teams Act Like Owners
C4Media
 
PDF
Does Java Need Inline Types? What Project Valhalla Can Bring to Java
C4Media
 
PDF
Service Meshes- The Ultimate Guide
C4Media
 
PDF
Shifting Left with Cloud Native CI/CD
C4Media
 
PDF
CI/CD for Machine Learning
C4Media
 
PDF
Fault Tolerance at Speed
C4Media
 
PDF
Architectures That Scale Deep - Regaining Control in Deep Systems
C4Media
 
PDF
ML in the Browser: Interactive Experiences with Tensorflow.js
C4Media
 
PDF
Build Your Own WebAssembly Compiler
C4Media
 
PDF
User & Device Identity for Microservices @ Netflix Scale
C4Media
 
PDF
Scaling Patterns for Netflix's Edge
C4Media
 
PDF
Make Your Electron App Feel at Home Everywhere
C4Media
 
PDF
The Talk You've Been Await-ing For
C4Media
 
PDF
Future of Data Engineering
C4Media
 
PDF
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
C4Media
 
Streaming a Million Likes/Second: Real-Time Interactions on Live Video
C4Media
 
Next Generation Client APIs in Envoy Mobile
C4Media
 
Software Teams and Teamwork Trends Report Q1 2020
C4Media
 
Understand the Trade-offs Using Compilers for Java Applications
C4Media
 
Kafka Needs No Keeper
C4Media
 
High Performing Teams Act Like Owners
C4Media
 
Does Java Need Inline Types? What Project Valhalla Can Bring to Java
C4Media
 
Service Meshes- The Ultimate Guide
C4Media
 
Shifting Left with Cloud Native CI/CD
C4Media
 
CI/CD for Machine Learning
C4Media
 
Fault Tolerance at Speed
C4Media
 
Architectures That Scale Deep - Regaining Control in Deep Systems
C4Media
 
ML in the Browser: Interactive Experiences with Tensorflow.js
C4Media
 
Build Your Own WebAssembly Compiler
C4Media
 
User & Device Identity for Microservices @ Netflix Scale
C4Media
 
Scaling Patterns for Netflix's Edge
C4Media
 
Make Your Electron App Feel at Home Everywhere
C4Media
 
The Talk You've Been Await-ing For
C4Media
 
Future of Data Engineering
C4Media
 
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
C4Media
 

Recently uploaded (20)

PPTX
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
Fwdays
 
PDF
Fl Studio 24.2.2 Build 4597 Crack for Windows Free Download 2025
faizk77g
 
PDF
Presentation - Vibe Coding The Future of Tech
yanuarsinggih1
 
PDF
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
PDF
Jak MŚP w Europie Środkowo-Wschodniej odnajdują się w świecie AI
dominikamizerska1
 
PDF
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
PDF
CIFDAQ Weekly Market Wrap for 11th July 2025
CIFDAQ
 
PDF
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
PDF
HCIP-Data Center Facility Deployment V2.0 Training Material (Without Remarks ...
mcastillo49
 
PDF
Log-Based Anomaly Detection: Enhancing System Reliability with Machine Learning
Mohammed BEKKOUCHE
 
PDF
Blockchain Transactions Explained For Everyone
CIFDAQ
 
PDF
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PDF
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
PDF
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
PDF
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
PDF
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
PDF
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
PPTX
UiPath Academic Alliance Educator Panels: Session 2 - Business Analyst Content
DianaGray10
 
PDF
From Code to Challenge: Crafting Skill-Based Games That Engage and Reward
aiyshauae
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
Fwdays
 
Fl Studio 24.2.2 Build 4597 Crack for Windows Free Download 2025
faizk77g
 
Presentation - Vibe Coding The Future of Tech
yanuarsinggih1
 
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
Jak MŚP w Europie Środkowo-Wschodniej odnajdują się w świecie AI
dominikamizerska1
 
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
CIFDAQ Weekly Market Wrap for 11th July 2025
CIFDAQ
 
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
HCIP-Data Center Facility Deployment V2.0 Training Material (Without Remarks ...
mcastillo49
 
Log-Based Anomaly Detection: Enhancing System Reliability with Machine Learning
Mohammed BEKKOUCHE
 
Blockchain Transactions Explained For Everyone
CIFDAQ
 
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
UiPath Academic Alliance Educator Panels: Session 2 - Business Analyst Content
DianaGray10
 
From Code to Challenge: Crafting Skill-Based Games That Engage and Reward
aiyshauae
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 

An Introduction to Property Based Testing

  • 1. QCon London 2016 An Introduction to Property Based Testing Aaron Bedra Chief Security Officer, Eligible @abedra
  • 2. InfoQ.com: News & Community Site • 750,000 unique visitors/month • Published in 4 languages (English, Chinese, Japanese and Brazilian Portuguese) • Post content from our QCon conferences • News 15-20 / week • Articles 3-4 / week • Presentations (videos) 12-15 / week • Interviews 2-3 / week • Books 1 / month Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations /property-based-testing-2016
  • 3. Purpose of QCon - to empower software development by facilitating the spread of knowledge and innovation Strategy - practitioner-driven conference designed for YOU: influencers of change and innovation in your teams - speakers and topics driving the evolution and innovation - connecting and catalyzing the influencers and innovators Highlights - attended by more than 12,000 delegates since 2007 - held in 9 cities worldwide Presented at QCon London www.qconlondon.com
  • 4. Why do we test? • To better understand what we are building • To help us think deeper about what we are building • To ensure the correctness of what we are building • To help us explore our design* • To explain to others how our code should work
  • 5. How do we test? • With compilers (type systems, static analysis, etc) • Manual testing • X-Unit style tests • Property/generative based tests • Formal modeling
  • 6. How do we test? • With compilers (type systems, static analysis, etc) • Manual testing • X-Unit style tests • Property/generative based tests • Formal modeling
  • 9. Property based testing eliminates the guess work on value and order of operations testing
  • 11. Instead of specifying how you specify what
  • 13. When we start our test suite, things are usually easy to understand
  • 14. public class Basic { public static Integer calculate(Integer x, Integer y) { return x + y; } }
  • 15. public class BasicTest { @Test public void TestCalculate() { assertEquals(Integer.valueOf(5), Basic.calculate(3, 2)); } }
  • 16. What other tests might we write for this code?
  • 17. Like all programs we start simple
  • 18. But over time things get more complicated
  • 19. What happens when our simple calculate function grows to include an entire domain?
  • 20. Our test suite will undoubtedly grow, but we have options to control the growth
  • 23. By changing our mental model just a bit we can cover much more ground
  • 24. Let’s revisit our basic example
  • 25. public class Basic { public static Integer calculate(Integer x, Integer y) { return x + y; } }
  • 26. But instead of a unit test, let’s write a property
  • 27. @RunWith(JUnitQuickcheck.class) public class BasicProperties { @Property public void calculateBaseAssumption(Integer x, Integer y) { Integer expected = x + y; assertEquals(expected, Basic.calculate(x, y)); } } public class BasicTest { @Test public void TestCalculate() { assertEquals(Integer.valueOf(5), Basic.calculate(3, 2)); } }
  • 28. @RunWith(JUnitQuickcheck.class) public class BasicProperties { @Property(trials = 1000000) public void calculateBaseAssumption(Integer x, Integer y) { Integer expected = x + y; assertEquals(expected, Basic.calculate(x, y)); } }
  • 29. This property isn’t much different than the unit test we had before it
  • 30. It’s just one level of abstraction higher
  • 31. Let’s add a constraint to our calculator
  • 32. Let’s say that the output cannot be negative
  • 33. public class Basic { public static Integer calculate(Integer x, Integer y) { Integer total = x + y; if (total < 0) { return 0; } else { return total; } } } java.lang.AssertionError: Property calculateBaseAssumption falsified for args shrunken to [0, -679447654]
  • 35. @RunWith(JUnitQuickcheck.class) public class BasicProperties { @Property public void calculateBaseAssumption(Integer x, Integer y) { Integer expected = x + y; assertEquals(expected, Basic.calculate(x, y)); } } public class Basic { public static Integer calculate(Integer x, Integer y) { Integer total = x + y; if (total < 0) { return 0; } else { return total; } } }
  • 36. Now we can be more specific with our property
  • 37. @RunWith(JUnitQuickcheck.class) public class BasicProperties { @Property public void calculateBaseAssumption(Integer x, Integer y) { assumeThat(x, greaterThan(0)); assumeThat(y, greaterThan(0)); assertThat(Basic.calculate(x, y), is(greaterThan(0))); } } java.lang.AssertionError: Property calculateBaseAssumption falsified for args shrunken to [647853159, 1499681379]
  • 38. We could keep going from here but let’s dive into some of the concepts
  • 40. This is one of my favorite use cases for invoking property based testing
  • 43. It ensures you have exact feature parity
  • 46. You can use them for all kinds of things
  • 48. Every route in your web application
  • 49. You could define generators based on your routes
  • 50. And create valid and invalid inputs for every endpoint
  • 51. You could run the generators on every test
  • 52. Or save the output of the generation for faster execution
  • 53. Saved execution of generators can even bring you to simulation testing
  • 54. There are tons of property based testing libraries available
  • 55. But this is a talk in a functional language track
  • 56. So let’s have some fun
  • 57. Let’s pretend we have some legacy code
  • 59. And we want to test it to make sure it actually works
  • 60. But there are no quickcheck libraries available*
  • 61. Warning! The crypto you are about to see should not be attempted at work
  • 63. Let’s start with our implementation
  • 64. #include <stdlib.h> #include <string.h> #include <ctype.h> char *caesar(int shift, char *input) { char *output = malloc(strlen(input)); memset(output, '0', strlen(input)); for (int x = 0; x < strlen(input); x++) { if (isalpha(input[x])) { int c = toupper(input[x]); c = (((c - 65) + shift) % 26) + 65; output[x] = c; } else { output[x] = input[x]; } } return output; }
  • 65. Next we create a new implementation to test against
  • 66. caesar :: Int -> String -> String caesar k = map f where f c | inRange ('A', 'Z') c = chr $ ord 'A' + (ord c - ord 'A' + k) `mod` 26 | otherwise = c
  • 67. We now have two functions that “should” do the same thing
  • 68. But they aren’t in the same language
  • 70. foreign import ccall "caesar.h caesar" c_caesar :: CInt -> CString -> CString native_caesar :: Int -> String -> IO String native_caesar shift input = withCString input $ c_str -> peekCString(c_caesar (fromIntegral shift) c_str)
  • 71. $ stack exec ghci caesar.hs caesar.so GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help [1 of 1] Compiling Main ( caesar.hs, interpreted ) Ok, modules loaded: Main. *Main> caesar 2 "ATTACKATDAWN" "CVVCEMCVFCYP" *Main> native_caesar 2 "ATTACKATDAWN" "CVVCEMCVFCYP"
  • 72. We can now execute our C code from inside of Haskell
  • 73. We can use Haskell’s quickcheck library to verify our C code
  • 74. First we need to write a property
  • 75. unsafeEq :: IO String -> String -> Bool unsafeEq x y = unsafePerformIO(x) == y genSafeChar :: Gen Char genSafeChar = elements ['A' .. 'Z'] genSafeString :: Gen String genSafeString = listOf genSafeChar newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString equivalenceProperty = forAll genSafeString $ str -> unsafeEq (native_caesar 2 str) (caesar 2 str)
  • 76. unsafeEq :: IO String -> String -> Bool unsafeEq x y = unsafePerformIO(x) == y genSafeChar :: Gen Char genSafeChar = elements ['A' .. 'Z'] genSafeString :: Gen String genSafeString = listOf genSafeChar newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString equivalenceProperty = forAll genSafeString $ str -> unsafeEq (native_caesar 2 str) (caesar 2 str)
  • 77. unsafeEq :: IO String -> String -> Bool unsafeEq x y = unsafePerformIO(x) == y genSafeChar :: Gen Char genSafeChar = elements ['A' .. 'Z'] genSafeString :: Gen String genSafeString = listOf genSafeChar newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString equivalenceProperty = forAll genSafeString $ str -> unsafeEq (native_caesar 2 str) (caesar 2 str)
  • 78. unsafeEq :: IO String -> String -> Bool unsafeEq x y = unsafePerformIO(x) == y genSafeChar :: Gen Char genSafeChar = elements ['A' .. 'Z'] genSafeString :: Gen String genSafeString = listOf genSafeChar newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString equivalenceProperty = forAll genSafeString $ str -> unsafeEq (native_caesar 2 str) (caesar 2 str)
  • 79. unsafeEq :: IO String -> String -> Bool unsafeEq x y = unsafePerformIO(x) == y genSafeChar :: Gen Char genSafeChar = elements ['A' .. 'Z'] genSafeString :: Gen String genSafeString = listOf genSafeChar newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString equivalenceProperty = forAll genSafeString $ str -> unsafeEq (native_caesar 2 str) (caesar 2 str)
  • 80. unsafeEq :: IO String -> String -> Bool unsafeEq x y = unsafePerformIO(x) == y genSafeChar :: Gen Char genSafeChar = elements ['A' .. 'Z'] genSafeString :: Gen String genSafeString = listOf genSafeChar newtype SafeString = SafeString { unwrapSafeString :: String } deriving Show instance Arbitrary SafeString where arbitrary = SafeString <$> genSafeString equivalenceProperty = forAll genSafeString $ str -> unsafeEq (native_caesar 2 str) (caesar 2 str)
  • 81. *Main> quickCheck equivalenceProperty *** Failed! Falsifiable (after 20 tests): "QYMSMCWTIXNDFDMLSL" *Main> caesar 2 "QYMSMCWTIXNDFDMLSL" "SAOUOEYVKZPFHFONUN" *Main> native_caesar 2 "QYMSMCWTIXNDFDMLSL" “SAOUOEYVKZPFHFONUN/Users/abedra/x“
  • 82. #include <stdlib.h> #include <string.h> #include <ctype.h> char *caesar(int shift, char *input) { char *output = malloc(strlen(input)); memset(output, '0', strlen(input)); for (int x = 0; x < strlen(input); x++) { if (isalpha(input[x])) { int c = toupper(input[x]); c = (((c - 65) + shift) % 26) + 65; output[x] = c; } else { output[x] = input[x]; } } return output; }
  • 83. We’ve found a memory handling issue in our C code!
  • 84. In reality there are more issues with this code, but our issue was quickly exposed
  • 87. Not all testing is created equal
  • 88. You should use as many different testing techniques as you need
  • 89. Remember to think about the limits of your tools
  • 90. And use tools that help you achieve your results more effectively
  • 93. Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations/property- based-testing-2016