SlideShare a Scribd company logo
Killing the Unit test
dor sever @ bigpanda
Killing the unit test - @dor_sever 1
The missing
Unit Test problem
2
Solving the missing unit test problem
Real life example: from Unit Test to Property Based Tests.
3
4
5
6
7
8
Implement counters for folders
9
10
sealed trait Folder
case object Active extends Folder
case object Shared extends Folder
case object Resolved extends Folder
11
12
case class Alert(data: String, folders: Set[Folder])
13
The requirements
» Add Alerts to current folder counters.
» Remove Alerts from folder counters.
» Direct access to a folder's count.
14
type Counters = Map[Folder, Int]
trait CountersApi {
def addToCounters(current: Counters,
alert: Alert): Counters
def removeFromCounters(current: Counters,
alert: Alert): Counters
}
15
type Counters = Map[Folder, Int]
trait CountersApi {
def addToCounters(current: Counters,
alert: Alert): Counters
def removeFromCounters(current: Counters,
alert: Alert): Counters
}
16
17
Imports
import org.scalatest._
trait TestSpec extends WordSpecLike with Matchers
import CountersApiV1Instance.addToCounters
18
class FolderCountersSpec extends TestSpec {
"addToCounters" should {
"increment the Active count by 1 when adding an Active alert" in {}
"increment the Shared count by 1 when adding a Shared alert" in {}
"increment the Resolved count by 1 when adding a Resolved alert" in {}
"increment all folders counts by 1 when adding alert that sits on all folders" in {}
}
"removeFromCounters" should {
"decrement the Active count by 1 when removing an Active alert" in {}
"decrement the Shared count by 1 when removing a Shared alert" in {}
"decrement all counts by 1 when removing an alert that sits on all folders" in {}
}
}
19
class FolderCountersSpec extends TestSpec {
val emptyCounters = Map[Folder,Int]()
"addToCounters" should {
"increment the Active count by 1 when adding an Active alert" in {
val activeAlert =
Alert("High CPU in elastic 11", Set(Active))
addToCounters(emptyCounters, activeAlert) shouldEqual Map(Active -> 1)
}
}
}
20
21
22
23
The bug
24
25
The original test
26
27
The missing unit test problem
28
29
Property Based Tests
» Property based tests work by defining a property
30
Property Based Tests
» Property based tests work by defining a property
which is a high-level specification of behavior
31
Property Based Tests
» Property based tests work by defining a property,
which is a high-level specification of behavior
that should hold for all values of the specific type.
32
Property Based Tests
» Property based tests work by defining a property,
which is a high-level specification of behavior
that should hold for all values of the specific type.
» ∀ value : T => property(value) == true
33
The white lie
» We can't really test all values.
» We generate 100 random inputs, and verify the property on
them.
34
my first property
» For all lists, if we reverse them twice, we should get the
original list.
35
List("my","first","property").reverse.reverse
36
scala> List("my","first","property").reverse.reverse
res1: List[String] = List(my, first, property)
37
List(1,1,2,3,5,8,13,21).reverse.reverse
38
scala> List(1,1,2,3,5,8,13,21).reverse.reverse
res3: List[Int] = List(1, 1, 2, 3, 5, 8, 13, 21)
39
Property based tests - The mechanics
» Scala check
» Generators
» Properties
40
trait Generator[A] {
def sample: A
}
41
Int generator
object intGen extends Generator[Int] {
override def sample: Int = scala.util.Random.nextInt
}
scala> intGen.sample
res4: Int = -1668959352
scala> intGen.sample
res5: Int = 309764036
scala> intGen.sample
res6: Int = -1278707761
42
Let's Generate Counters
scala> type Counters = Map[Folder, Int]
defined type alias Counters
43
import org.scalacheck.Gen
val folderToCounterGen =
for {
folder <- Gen.oneOf(Active, Resolved, Shared)
counter <- Gen.posNum[Int]
} yield folder -> counter
implicit val countersGen: Gen[Map[Folder, Int]] =
Gen.listOf(folderToCounterGen).map(_.toMap)
44
scala> countersGen.sample
res7: Option[Map[Folder,Int]] = Some(Map(Active -> 42, Shared -> 84, Resolved -> 33))
scala> countersGen.sample
res8: Option[Map[Folder,Int]] = Some(Map(Resolved -> 38, Active -> 11, Shared -> 36))
45
Properties
46
Property = Assertion ?
47
What is a property ?
» Properties are assertions with special requirements
» They must hold for all values!
48
Imports
import org.scalatest.prop.GeneratorDrivenPropertyChecks
trait FolderPropertiesSpec extends WordSpecLike
with Matchers
with GeneratorDrivenPropertyChecks
import CountersApiV1Instance.addToCounters
49
The first property
class FolderCountersPropertySpec extends FolderPropertiesSpec {
"increment the alert folders" in forAll {
(counters: Map[Folder, Int], alert: Alert) =>
addToCounters(counters, alert) shouldEqual ???
}
}
50
class FolderCountersPropertySpec extends FolderPropertiesSpec {
"increment the alert folders" in forAll {
(counters: Map[Folder, Int], alert: Alert) =>
addToCounters(counters, alert) shouldEqual ???
}
}
51
class FolderCountersPropertySpec extends FolderPropertiesSpec {
"increment the alert folders" in forAll {
(counters: Map[Folder, Int], alert: Alert) =>
addToCounters(counters, alert) shouldEqual ???
}
}
52
How do we define properties?
53
54
Empty alert Property
55
56
class FolderCountersPropertySpec extends FolderPropertiesSpec {
"adding the empty alert should not change the counters" in forAll {
counters: Map[Folder, Int] =>
addToCounters(counters, Alert("",Set())) shouldEqual counters
}
}
57
58
Victory ?
59
Remove From Counters
» Repeat the process
60
The missing properties problem
How to find properties?
trait CountersApi {
def addToCounters(current: Counters, alert: Alert): Counters
def removeFromCounters(current: Counters, alert: Alert): Counters
}
61
Killing the unit test - @dor_sever 62
Remove and Add are related!
» Removing and then adding an alert should keep the original
counters
class FolderCountersPropertySpecWorking extends FolderPropertiesSpec {
"Removing and then adding an alert should keep the original counters" in forAll {
(counters: Map[Folder, Int], alert: Alert) =>
addToCounters(removeFromCounters(counters, alert), alert) shouldEqual counters
}
}
63
class FolderCountersPropertySpecWorking extends FolderPropertiesSpec {
"Removing and then adding an alert should keep the original counters" in forAll {
(counters: Map[Folder, Int], alert: Alert) =>
addToCounters(removeFromCounters(counters, alert), alert) shouldEqual counters
}
}
64
class FolderCountersPropertySpecWorking extends FolderPropertiesSpec {
"Removing and then adding an alert should keep the original counters" in forAll {
(counters: Map[Folder, Int], alert: Alert) =>
addToCounters(removeFromCounters(counters, alert), alert) shouldEqual counters
}
}
65
66
67
Making it fail consistently
class FolderCountersPropertySpecWorking extends FolderPropertiesSpec {
"Removing and adding an alert should keep the original counters" in forAll(minSuccessful(1000)) {
(counters: Map[Folder, Int], alert: Alert) =>
addToCounters(removeFromCounters(counters, alert), alert) shouldEqual counters
}
}
68
Let's define
val sharedAlert = Alert("",Set(Shared))
val emptyCounters = Map[Folder,Int]()
69
The BUG in our property
scala> val removedCounters = removeFromCounters(emptyCounters,sharedAlert)
removedCounters: Counters = Map()
70
The BUG in our property
scala> val removedCounters = removeFromCounters(emptyCounters,sharedAlert)
removedCounters: Counters = Map()
scala> val result = addToCounters(removedCounters,sharedAlert)
result: Counters = Map(Shared -> 1)
71
The BUG in our property
scala> val removedCounters = removeFromCounters(emptyCounters,sharedAlert)
removedCounters: Counters = Map()
scala> val result = addToCounters(removedCounters,sharedAlert)
result: Counters = Map(Shared -> 1)
scala> result == emptyCounters
res0: Boolean = false
72
The BUG in our property
scala> val removedCounters = removeFromCounters(emptyCounters,sharedAlert)
removedCounters: Counters = Map()
scala> val result = addToCounters(removedCounters,sharedAlert)
result: Counters = Map(Shared -> 1)
scala> Map(Shared->1) == Map()
res1: Boolean = false
73
The Good property?
class FolderCountersPropertySpecWorking extends FolderPropertiesSpec {
"Adding and then removing an alert should keep the original counters" in forAll{
(counters: Map[Folder, Int], alert: Alert) =>
removeFromCounters(addToCounters(counters,alert),alert) shouldEqual counters
}
}
74
A different bug
scala> val addedAlerts = addToCounters(emptyCounters,sharedAlert)
addedAlerts: Counters = Map(Shared -> 1)
75
A different bug
scala> val addedAlerts = addToCounters(emptyCounters,sharedAlert)
addedAlerts: Counters = Map(Shared -> 1)
scala> val result = removeFromCounters(addedAlerts,sharedAlert)
result: Counters = Map(Shared -> 0)
76
A different bug
scala> val addedAlerts = addToCounters(emptyCounters,sharedAlert)
addedAlerts: Counters = Map(Shared -> 1)
scala> val result = removeFromCounters(addedAlerts,sharedAlert)
result: Counters = Map(Shared -> 0)
scala> result == emptyCounters
res2: Boolean = false
77
A different bug
scala> val addedAlerts = addToCounters(emptyCounters,sharedAlert)
addedAlerts: Counters = Map(Shared -> 1)
scala> val result = removeFromCounters(addedAlerts,sharedAlert)
result: Counters = Map(Shared -> 0)
scala> Map(Shared->0) == Map()
res3: Boolean = false
78
Let's fix it?
79
Victory?
80
Group to the rescue
» Disclaimer:
» this is going to require a leap of faith
» this will not work at all times
» This is going to be fast and fun
81
The Plan
82
83
trait myGroup[A] {
def add(x: A, y: A): A
def remove(x: A, y: A): A
def inverse(x: A): A
def identity: A
}
84
trait CountersApi {
def addToCounters(current: Counters, alert: Alert): Counters
def removeFromCounters(current: Counters, alert: Alert): Counters
}
85
Getting rid of the alert data type
86
Getting rid of the alert data type
def toCounters(alert:Alert):Counters = ???
87
trait CountersApi {
def addToCounters(current: Counters, added: Counters): Counters
def removeFromCounters(current: Counters, removed: Counters): Counters
}
88
Identity Element
89
trait CountersApi {
def addToCounters(current: Counters, added: Counters): Counters
def removeFromCounters(current: Counters, removed: Counters): Counters
def identity : Counters
}
90
Making the API polymorphic
trait CountersApi[A] {
def addToCounters(current: A, added: A): A
def removeFromCounters(current: A, removed: A): A
def identity : A
}
91
The inverse function
92
trait CountersApi[A] {
def addToCounters(current: A, added: A): A
def removeFromCounters(current: A, removed: A): A
def inverse(x: A) : A
def identity : A
}
93
trait CountersApi[A] {
def combine(current: A, added: A): A
def remove(current: A, removed: A): A =
combine(current,inverse(removed))
def inverse(x:A): A
def identity : A
}
94
Implementation
95
The tests!
import cats.laws.discipline._
import cats.kernel.laws.discipline.GroupTests
import org.scalatest.FunSuiteLike
import org.typelevel.discipline.scalatest.Discipline
class GroupsTests extends Discipline with FunSuiteLike {
checkAll("CountersGroupTests", GroupTests[Counters].group)
}
96
97
98
99
Summary
» The missing unit test problem
» Property based tests
» Generators
» Properties, the missing properties problem
» Math to the rescue!
Killing the unit test - @dor_sever 100
Takeaways
» Try this at home !
» Huge payoff, and it's super fun
Killing the unit test - @dor_sever 101
Thank you
» https://github.com/dorsev/Killing-the-unit-test-talk
» https://medium.com/free-code-camp/an-introduction-to-
law-testing-in-scala-4243d72272f9
Killing the unit test - @dor_sever 102

More Related Content

What's hot

Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)stasimus
 
Tupple ware in scala
Tupple ware in scalaTupple ware in scala
Tupple ware in scala
VulcanMinds
 
Basic data structures in python
Basic data structures in pythonBasic data structures in python
Basic data structures in python
Lifna C.S
 
Introduction to matlab
Introduction to matlabIntroduction to matlab
Introduction to matlabkrishna_093
 
Second chapter-java
Second chapter-javaSecond chapter-java
Second chapter-java
Ahmad sohail Kakar
 
Scala parallel-collections
Scala parallel-collectionsScala parallel-collections
Scala parallel-collections
Knoldus Inc.
 
Compact and safely: static DSL on Kotlin
Compact and safely: static DSL on KotlinCompact and safely: static DSL on Kotlin
Compact and safely: static DSL on Kotlin
Dmitry Pranchuk
 
A tour of Python
A tour of PythonA tour of Python
A tour of Python
Aleksandar Veselinovic
 
37c
37c37c
Scala. Introduction to FP. Monads
Scala. Introduction to FP. MonadsScala. Introduction to FP. Monads
Scala. Introduction to FP. Monads
Kirill Kozlov
 
Java 8 - An Introduction by Jason Swartz
Java 8 - An Introduction by Jason SwartzJava 8 - An Introduction by Jason Swartz
Java 8 - An Introduction by Jason Swartz
Jason Swartz
 
Type classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceType classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritance
Alexey Raga
 
Windows 7
Windows 7Windows 7
Windows 7
punheta1000
 
E10
E10E10
E10
lksoo
 
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)
Eric Torreborre
 
Map, Reduce and Filter in Swift
Map, Reduce and Filter in SwiftMap, Reduce and Filter in Swift
Map, Reduce and Filter in Swift
Aleksandras Smirnovas
 
Fp java8
Fp java8Fp java8
Fp java8
Yanai Franchi
 
Arrays in python
Arrays in pythonArrays in python
Arrays in python
Lifna C.S
 
Ml all programs
Ml all programsMl all programs
Ml all programs
adnaanmohamed
 

What's hot (20)

Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)
 
Tupple ware in scala
Tupple ware in scalaTupple ware in scala
Tupple ware in scala
 
Basic data structures in python
Basic data structures in pythonBasic data structures in python
Basic data structures in python
 
Introduction to matlab
Introduction to matlabIntroduction to matlab
Introduction to matlab
 
Second chapter-java
Second chapter-javaSecond chapter-java
Second chapter-java
 
Scala parallel-collections
Scala parallel-collectionsScala parallel-collections
Scala parallel-collections
 
Compact and safely: static DSL on Kotlin
Compact and safely: static DSL on KotlinCompact and safely: static DSL on Kotlin
Compact and safely: static DSL on Kotlin
 
A tour of Python
A tour of PythonA tour of Python
A tour of Python
 
37c
37c37c
37c
 
Scala. Introduction to FP. Monads
Scala. Introduction to FP. MonadsScala. Introduction to FP. Monads
Scala. Introduction to FP. Monads
 
Java 8 - An Introduction by Jason Swartz
Java 8 - An Introduction by Jason SwartzJava 8 - An Introduction by Jason Swartz
Java 8 - An Introduction by Jason Swartz
 
Type classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceType classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritance
 
Windows 7
Windows 7Windows 7
Windows 7
 
E10
E10E10
E10
 
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)
 
Map, Reduce and Filter in Swift
Map, Reduce and Filter in SwiftMap, Reduce and Filter in Swift
Map, Reduce and Filter in Swift
 
Fp java8
Fp java8Fp java8
Fp java8
 
Arrays in python
Arrays in pythonArrays in python
Arrays in python
 
Ann
AnnAnn
Ann
 
Ml all programs
Ml all programsMl all programs
Ml all programs
 

Similar to Killing The Unit test talk

Intelligent System Optimizations
Intelligent System OptimizationsIntelligent System Optimizations
Intelligent System Optimizations
Martin Zapletal
 
Lecture 5: Functional Programming
Lecture 5: Functional ProgrammingLecture 5: Functional Programming
Lecture 5: Functional ProgrammingEelco Visser
 
Introduction to matlab
Introduction to matlabIntroduction to matlab
Introduction to matlab
BilawalBaloch1
 
ScalaMeter 2012
ScalaMeter 2012ScalaMeter 2012
ScalaMeter 2012
Aleksandar Prokopec
 
Array
ArrayArray
ScalaMeter 2014
ScalaMeter 2014ScalaMeter 2014
ScalaMeter 2014
Aleksandar Prokopec
 
Python3 cheatsheet
Python3 cheatsheetPython3 cheatsheet
Python3 cheatsheet
Gil Cohen
 
Funtional Reactive Programming with Examples in Scala + GWT
Funtional Reactive Programming with Examples in Scala + GWTFuntional Reactive Programming with Examples in Scala + GWT
Funtional Reactive Programming with Examples in Scala + GWT
Vasil Remeniuk
 
Python_ 3 CheatSheet
Python_ 3 CheatSheetPython_ 3 CheatSheet
Python_ 3 CheatSheet
Dr. Volkan OBAN
 
Vector3
Vector3Vector3
Vector3
Rajendran
 
Monadologie
MonadologieMonadologie
Monadologie
league
 
Python3
Python3Python3
Mementopython3 english
Mementopython3 englishMementopython3 english
Mementopython3 english
ssuser442080
 
Lec 9 05_sept [compatibility mode]
Lec 9 05_sept [compatibility mode]Lec 9 05_sept [compatibility mode]
Lec 9 05_sept [compatibility mode]Palak Sanghani
 
Using-Python-Libraries.9485146.powerpoint.pptx
Using-Python-Libraries.9485146.powerpoint.pptxUsing-Python-Libraries.9485146.powerpoint.pptx
Using-Python-Libraries.9485146.powerpoint.pptx
UadAccount
 
Underscore.js
Underscore.jsUnderscore.js
Underscore.jstimourian
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기
진성 오
 
Meet scala
Meet scalaMeet scala
Meet scala
Wojciech Pituła
 

Similar to Killing The Unit test talk (20)

Intelligent System Optimizations
Intelligent System OptimizationsIntelligent System Optimizations
Intelligent System Optimizations
 
An introduction to scala
An introduction to scalaAn introduction to scala
An introduction to scala
 
Lecture 5: Functional Programming
Lecture 5: Functional ProgrammingLecture 5: Functional Programming
Lecture 5: Functional Programming
 
Introduction to matlab
Introduction to matlabIntroduction to matlab
Introduction to matlab
 
ScalaMeter 2012
ScalaMeter 2012ScalaMeter 2012
ScalaMeter 2012
 
Array
ArrayArray
Array
 
ScalaMeter 2014
ScalaMeter 2014ScalaMeter 2014
ScalaMeter 2014
 
Python3 cheatsheet
Python3 cheatsheetPython3 cheatsheet
Python3 cheatsheet
 
Funtional Reactive Programming with Examples in Scala + GWT
Funtional Reactive Programming with Examples in Scala + GWTFuntional Reactive Programming with Examples in Scala + GWT
Funtional Reactive Programming with Examples in Scala + GWT
 
Python_ 3 CheatSheet
Python_ 3 CheatSheetPython_ 3 CheatSheet
Python_ 3 CheatSheet
 
Vector3
Vector3Vector3
Vector3
 
Monadologie
MonadologieMonadologie
Monadologie
 
Python3
Python3Python3
Python3
 
Mementopython3 english
Mementopython3 englishMementopython3 english
Mementopython3 english
 
bobok
bobokbobok
bobok
 
Lec 9 05_sept [compatibility mode]
Lec 9 05_sept [compatibility mode]Lec 9 05_sept [compatibility mode]
Lec 9 05_sept [compatibility mode]
 
Using-Python-Libraries.9485146.powerpoint.pptx
Using-Python-Libraries.9485146.powerpoint.pptxUsing-Python-Libraries.9485146.powerpoint.pptx
Using-Python-Libraries.9485146.powerpoint.pptx
 
Underscore.js
Underscore.jsUnderscore.js
Underscore.js
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기
 
Meet scala
Meet scalaMeet scala
Meet scala
 

Recently uploaded

Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Anthony Dahanne
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
Globus
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
Globus
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
Globus
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with Platformless
WSO2
 
Large Language Models and the End of Programming
Large Language Models and the End of ProgrammingLarge Language Models and the End of Programming
Large Language Models and the End of Programming
Matt Welsh
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
Globus
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
XfilesPro
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
vrstrong314
 
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology SolutionsProsigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Natan Silnitsky
 
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
Juraj Vysvader
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
Ortus Solutions, Corp
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
wottaspaceseo
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Globus
 
2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx
Georgi Kodinov
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
Globus
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
IES VE
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
Tier1 app
 

Recently uploaded (20)

Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with Platformless
 
Large Language Models and the End of Programming
Large Language Models and the End of ProgrammingLarge Language Models and the End of Programming
Large Language Models and the End of Programming
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology SolutionsProsigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology Solutions
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
 
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
 
2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 

Killing The Unit test talk

  • 1. Killing the Unit test dor sever @ bigpanda Killing the unit test - @dor_sever 1
  • 3. Solving the missing unit test problem Real life example: from Unit Test to Property Based Tests. 3
  • 4. 4
  • 5. 5
  • 6. 6
  • 7. 7
  • 8. 8
  • 10. 10
  • 11. sealed trait Folder case object Active extends Folder case object Shared extends Folder case object Resolved extends Folder 11
  • 12. 12
  • 13. case class Alert(data: String, folders: Set[Folder]) 13
  • 14. The requirements » Add Alerts to current folder counters. » Remove Alerts from folder counters. » Direct access to a folder's count. 14
  • 15. type Counters = Map[Folder, Int] trait CountersApi { def addToCounters(current: Counters, alert: Alert): Counters def removeFromCounters(current: Counters, alert: Alert): Counters } 15
  • 16. type Counters = Map[Folder, Int] trait CountersApi { def addToCounters(current: Counters, alert: Alert): Counters def removeFromCounters(current: Counters, alert: Alert): Counters } 16
  • 17. 17
  • 18. Imports import org.scalatest._ trait TestSpec extends WordSpecLike with Matchers import CountersApiV1Instance.addToCounters 18
  • 19. class FolderCountersSpec extends TestSpec { "addToCounters" should { "increment the Active count by 1 when adding an Active alert" in {} "increment the Shared count by 1 when adding a Shared alert" in {} "increment the Resolved count by 1 when adding a Resolved alert" in {} "increment all folders counts by 1 when adding alert that sits on all folders" in {} } "removeFromCounters" should { "decrement the Active count by 1 when removing an Active alert" in {} "decrement the Shared count by 1 when removing a Shared alert" in {} "decrement all counts by 1 when removing an alert that sits on all folders" in {} } } 19
  • 20. class FolderCountersSpec extends TestSpec { val emptyCounters = Map[Folder,Int]() "addToCounters" should { "increment the Active count by 1 when adding an Active alert" in { val activeAlert = Alert("High CPU in elastic 11", Set(Active)) addToCounters(emptyCounters, activeAlert) shouldEqual Map(Active -> 1) } } } 20
  • 21. 21
  • 22. 22
  • 23. 23
  • 25. 25
  • 27. 27
  • 28. The missing unit test problem 28
  • 29. 29
  • 30. Property Based Tests » Property based tests work by defining a property 30
  • 31. Property Based Tests » Property based tests work by defining a property which is a high-level specification of behavior 31
  • 32. Property Based Tests » Property based tests work by defining a property, which is a high-level specification of behavior that should hold for all values of the specific type. 32
  • 33. Property Based Tests » Property based tests work by defining a property, which is a high-level specification of behavior that should hold for all values of the specific type. » ∀ value : T => property(value) == true 33
  • 34. The white lie » We can't really test all values. » We generate 100 random inputs, and verify the property on them. 34
  • 35. my first property » For all lists, if we reverse them twice, we should get the original list. 35
  • 40. Property based tests - The mechanics » Scala check » Generators » Properties 40
  • 41. trait Generator[A] { def sample: A } 41
  • 42. Int generator object intGen extends Generator[Int] { override def sample: Int = scala.util.Random.nextInt } scala> intGen.sample res4: Int = -1668959352 scala> intGen.sample res5: Int = 309764036 scala> intGen.sample res6: Int = -1278707761 42
  • 43. Let's Generate Counters scala> type Counters = Map[Folder, Int] defined type alias Counters 43
  • 44. import org.scalacheck.Gen val folderToCounterGen = for { folder <- Gen.oneOf(Active, Resolved, Shared) counter <- Gen.posNum[Int] } yield folder -> counter implicit val countersGen: Gen[Map[Folder, Int]] = Gen.listOf(folderToCounterGen).map(_.toMap) 44
  • 45. scala> countersGen.sample res7: Option[Map[Folder,Int]] = Some(Map(Active -> 42, Shared -> 84, Resolved -> 33)) scala> countersGen.sample res8: Option[Map[Folder,Int]] = Some(Map(Resolved -> 38, Active -> 11, Shared -> 36)) 45
  • 48. What is a property ? » Properties are assertions with special requirements » They must hold for all values! 48
  • 49. Imports import org.scalatest.prop.GeneratorDrivenPropertyChecks trait FolderPropertiesSpec extends WordSpecLike with Matchers with GeneratorDrivenPropertyChecks import CountersApiV1Instance.addToCounters 49
  • 50. The first property class FolderCountersPropertySpec extends FolderPropertiesSpec { "increment the alert folders" in forAll { (counters: Map[Folder, Int], alert: Alert) => addToCounters(counters, alert) shouldEqual ??? } } 50
  • 51. class FolderCountersPropertySpec extends FolderPropertiesSpec { "increment the alert folders" in forAll { (counters: Map[Folder, Int], alert: Alert) => addToCounters(counters, alert) shouldEqual ??? } } 51
  • 52. class FolderCountersPropertySpec extends FolderPropertiesSpec { "increment the alert folders" in forAll { (counters: Map[Folder, Int], alert: Alert) => addToCounters(counters, alert) shouldEqual ??? } } 52
  • 53. How do we define properties? 53
  • 54. 54
  • 56. 56
  • 57. class FolderCountersPropertySpec extends FolderPropertiesSpec { "adding the empty alert should not change the counters" in forAll { counters: Map[Folder, Int] => addToCounters(counters, Alert("",Set())) shouldEqual counters } } 57
  • 58. 58
  • 60. Remove From Counters » Repeat the process 60
  • 61. The missing properties problem How to find properties? trait CountersApi { def addToCounters(current: Counters, alert: Alert): Counters def removeFromCounters(current: Counters, alert: Alert): Counters } 61
  • 62. Killing the unit test - @dor_sever 62
  • 63. Remove and Add are related! » Removing and then adding an alert should keep the original counters class FolderCountersPropertySpecWorking extends FolderPropertiesSpec { "Removing and then adding an alert should keep the original counters" in forAll { (counters: Map[Folder, Int], alert: Alert) => addToCounters(removeFromCounters(counters, alert), alert) shouldEqual counters } } 63
  • 64. class FolderCountersPropertySpecWorking extends FolderPropertiesSpec { "Removing and then adding an alert should keep the original counters" in forAll { (counters: Map[Folder, Int], alert: Alert) => addToCounters(removeFromCounters(counters, alert), alert) shouldEqual counters } } 64
  • 65. class FolderCountersPropertySpecWorking extends FolderPropertiesSpec { "Removing and then adding an alert should keep the original counters" in forAll { (counters: Map[Folder, Int], alert: Alert) => addToCounters(removeFromCounters(counters, alert), alert) shouldEqual counters } } 65
  • 66. 66
  • 67. 67
  • 68. Making it fail consistently class FolderCountersPropertySpecWorking extends FolderPropertiesSpec { "Removing and adding an alert should keep the original counters" in forAll(minSuccessful(1000)) { (counters: Map[Folder, Int], alert: Alert) => addToCounters(removeFromCounters(counters, alert), alert) shouldEqual counters } } 68
  • 69. Let's define val sharedAlert = Alert("",Set(Shared)) val emptyCounters = Map[Folder,Int]() 69
  • 70. The BUG in our property scala> val removedCounters = removeFromCounters(emptyCounters,sharedAlert) removedCounters: Counters = Map() 70
  • 71. The BUG in our property scala> val removedCounters = removeFromCounters(emptyCounters,sharedAlert) removedCounters: Counters = Map() scala> val result = addToCounters(removedCounters,sharedAlert) result: Counters = Map(Shared -> 1) 71
  • 72. The BUG in our property scala> val removedCounters = removeFromCounters(emptyCounters,sharedAlert) removedCounters: Counters = Map() scala> val result = addToCounters(removedCounters,sharedAlert) result: Counters = Map(Shared -> 1) scala> result == emptyCounters res0: Boolean = false 72
  • 73. The BUG in our property scala> val removedCounters = removeFromCounters(emptyCounters,sharedAlert) removedCounters: Counters = Map() scala> val result = addToCounters(removedCounters,sharedAlert) result: Counters = Map(Shared -> 1) scala> Map(Shared->1) == Map() res1: Boolean = false 73
  • 74. The Good property? class FolderCountersPropertySpecWorking extends FolderPropertiesSpec { "Adding and then removing an alert should keep the original counters" in forAll{ (counters: Map[Folder, Int], alert: Alert) => removeFromCounters(addToCounters(counters,alert),alert) shouldEqual counters } } 74
  • 75. A different bug scala> val addedAlerts = addToCounters(emptyCounters,sharedAlert) addedAlerts: Counters = Map(Shared -> 1) 75
  • 76. A different bug scala> val addedAlerts = addToCounters(emptyCounters,sharedAlert) addedAlerts: Counters = Map(Shared -> 1) scala> val result = removeFromCounters(addedAlerts,sharedAlert) result: Counters = Map(Shared -> 0) 76
  • 77. A different bug scala> val addedAlerts = addToCounters(emptyCounters,sharedAlert) addedAlerts: Counters = Map(Shared -> 1) scala> val result = removeFromCounters(addedAlerts,sharedAlert) result: Counters = Map(Shared -> 0) scala> result == emptyCounters res2: Boolean = false 77
  • 78. A different bug scala> val addedAlerts = addToCounters(emptyCounters,sharedAlert) addedAlerts: Counters = Map(Shared -> 1) scala> val result = removeFromCounters(addedAlerts,sharedAlert) result: Counters = Map(Shared -> 0) scala> Map(Shared->0) == Map() res3: Boolean = false 78
  • 81. Group to the rescue » Disclaimer: » this is going to require a leap of faith » this will not work at all times » This is going to be fast and fun 81
  • 83. 83
  • 84. trait myGroup[A] { def add(x: A, y: A): A def remove(x: A, y: A): A def inverse(x: A): A def identity: A } 84
  • 85. trait CountersApi { def addToCounters(current: Counters, alert: Alert): Counters def removeFromCounters(current: Counters, alert: Alert): Counters } 85
  • 86. Getting rid of the alert data type 86
  • 87. Getting rid of the alert data type def toCounters(alert:Alert):Counters = ??? 87
  • 88. trait CountersApi { def addToCounters(current: Counters, added: Counters): Counters def removeFromCounters(current: Counters, removed: Counters): Counters } 88
  • 90. trait CountersApi { def addToCounters(current: Counters, added: Counters): Counters def removeFromCounters(current: Counters, removed: Counters): Counters def identity : Counters } 90
  • 91. Making the API polymorphic trait CountersApi[A] { def addToCounters(current: A, added: A): A def removeFromCounters(current: A, removed: A): A def identity : A } 91
  • 93. trait CountersApi[A] { def addToCounters(current: A, added: A): A def removeFromCounters(current: A, removed: A): A def inverse(x: A) : A def identity : A } 93
  • 94. trait CountersApi[A] { def combine(current: A, added: A): A def remove(current: A, removed: A): A = combine(current,inverse(removed)) def inverse(x:A): A def identity : A } 94
  • 96. The tests! import cats.laws.discipline._ import cats.kernel.laws.discipline.GroupTests import org.scalatest.FunSuiteLike import org.typelevel.discipline.scalatest.Discipline class GroupsTests extends Discipline with FunSuiteLike { checkAll("CountersGroupTests", GroupTests[Counters].group) } 96
  • 97. 97
  • 98. 98
  • 99. 99
  • 100. Summary » The missing unit test problem » Property based tests » Generators » Properties, the missing properties problem » Math to the rescue! Killing the unit test - @dor_sever 100
  • 101. Takeaways » Try this at home ! » Huge payoff, and it's super fun Killing the unit test - @dor_sever 101
  • 102. Thank you » https://github.com/dorsev/Killing-the-unit-test-talk » https://medium.com/free-code-camp/an-introduction-to- law-testing-in-scala-4243d72272f9 Killing the unit test - @dor_sever 102