SlideShare a Scribd company logo
Testing in the world of FP
Luka Jacobowitz - Lamdba World 2018
Software Developer at
codecentric
Co-organizer of ScalaDus
and IdrisDus
Maintainer of cats,
cats-effect, cats-mtl,
cats-tagless, OutWatch
Enthusiastic about FP
About me
Agenda ● Property-based testing
● Mocking
● Conclusions
Property-based testing is awesome
● It allows us to generate a bunch of test cases we would never have
been able to write by ourselves
● Makes sure even edge cases are well handled
● Can help us find bugs as early as possible
Property-based testing - Pains
/**
* Should use UUIDv1 and "yyyy-MM-dd HH:mm:ss" format
*/
def uuidCreatedAfter(uuid: String, date: String): Boolean = {
val arr = uuid.split("-")
val timestampHex = arr(2).substring(1) + arr(1) + arr(0)
...
}
Newtype it!
Common reasons against newtyping:
● It won’t support the operations I need
● It will lead to a lot of boilerplate conversions
● It will give us a performance penalty
scala-newtype
@newtype case class Euros(n: Int)
object Euros {
implicit val eurosNumeric: Numeric[Euros] = deriving
}
Euros(25) + Euros(10) - Euros(5)
Refined
val url: String Refined Url = "http://example.com"
val failed: String Refined Url = "hrrp://example.com"
error: Url predicate failed: unknown protocol: hrrp
val n: Int Refined Positive = 42
Refined-Scalacheck
import eu.timepit.refined.scalacheck.numeric._
def leftPad(s: String, n: Int Refined Positive): String
forAll { (s: String, n: Int Refined Positive) =>
assert(leftPad(s, n).length >= n)
}
Accepting only valid inputs for your
functions makes your code more precise
and allows for much easier property-based
testing
So many more gems out there
val n: Int Refined Positive =
NonEmptyList.of(1, 2, 3).refinedSize
val v: ValidatedNel[String, SHA1] =
SHA1.validate("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")
Use this instead
● A duration of time -> use FiniteDuration instead of Int
● A non-empty sequence -> use NonEmptyList instead of List
● A date -> use LocalDate instead of String
● An IP-address -> use String Refined IPv4 instead of String
● etc.
Other great libraries
● Libra - A dimensional analysis library, allows us to multiply two meter
values and receive a square meter value
● FUUID - Functional UUID library, gives us new random UUIDs in IO
and uses Either for construction from String
● Squants - Similar to Libra, but also comes with built in units of measure
such as KiloWatts, Tons and even Money
How do I test this?
def sendXml(s: String): Unit
def sendAll(list: List[String]): Unit = {
list.foreach(sendXml)
}
Split it up
def parseXml(s: String): Either[Throwable, Xml]
def sendXml(x: Xml): IO[Unit]
def parseAndSend(s: String): IO[Unit] =
IO.fromEither(parseXml(s)).flatMap(sendXml)
list.traverse_(parseAndSend)
Now we can test parsing, what about sending?
def sendXml(x: Xml): IO[Unit]
Testing what we don’t control
We have two (non mutually exclusive) options
1. Use integration tests to check if the behaviour does what we want
(can be difficult)
2. Mock the outside world and test expectations (easier, but
potentially less accurate)
How do we use mocking to test IO?
Tagless
Final
Tagless Final
● Allows us to separate problem description from the actual problem
solution and implementation
● This means we can use our own Algebras for defining interactions
● We can work at an extra level of abstraction but maintain flexibility
Tagless Final - How to
● Model our Algebras as traits parametrized with a type constructor
● Programs constrain the type parameter (e.g. with Monad)
● Interpreters are simply implementations of those traits
An example
def bookThings: IO[Unit] = for {
_ <- bookDrink(coke)
_ <- bookRoomService
_ <- bookSandwich(wholeWheat, hummus)
} yield ()
Using Tagless Final
trait BookingService[F[_]] {
def bookDrink(b: Beverage): F[Unit]
def bookRoomService: F[Unit]
def bookSandwich(d: Dough, t: Topping): F[Unit]
}
def bookThings[F[_]: Apply](F: BookingService[F]): F[Unit] =
F.bookDrink(coke) *>
F.bookRoomService *>
F.bookSandwich(wholeWheat, hummus)
A possible Test interpreter
sealed trait Booking
case class DrinkBooking(b: Beverage) extends Booking
case object RoomServiceBooking extends Booking
case class SandwichBooking(d: Dough, t: Topping) extends Booking
def testService = new BookingService[Const[List[Booking], ?]] {
def bookDrink(b: Beverage): Const[List[Booking], Unit] =
Const(List(DrinkBooking(b)))
def bookRoomService: Const[List[Booking], Unit] =
Const(List(RoomServiceBooking))
def bookSandwich(d: Dough, t: Topping): Const[List[Booking], Unit] =
Const(List(SandwichBooking(d, t)))
}
Running the program
val bookings: List[Booking] = bookThings(testService).getConst
val expectedBookings: List[Booking] = List(...)
assert(bookings === expectedBookings)
What are we testing here? The external system?
We’re only testing the interal wiring of our own system.
External world testing continued
Last time was too easy, we were just returning Unit!
Usually we have to deal with more complex return types that are much
harder to mock.
External world testing continued
Two questions:
1. What does your external service provide to you, is it just data or also
State?
2. Can you reasonably mimic the behaviour of the external service with a
self-contained state machine?
A complex example
def discountAll(dt: DiscountType): IO[List[Customer]] = for {
customers <- getAllCustomers
_ <- customers.traverse_(logCustomer)
discount <- getDiscount(dt)
_ <- logDiscount(dt, discount)
updated <- customers.traverse(c =>
updateDiscountIfEligible(discount, c))
_ <- updated.traverse_(logCustomer)
} yield updated
A complex example
trait Logging[F[_]] {
def logCustomer(c: Customer): F[Unit]
def logDiscount(dt: DiscountType, d: Discount): F[Unit]
}
A complex example
trait CustomerService[F[_]] {
def getAllCustomers: F[List[Customer]]
def getDiscount(dt: DiscountType): F[Discount]
def updateDiscountIfEligible(d: Discount, c: Customer): F[Customer]
}
def discountAll[F[_]: Monad](dt: DiscountType)
(F: CustomerService[F], L: Logging[F]): F[List[Customer]] = for {
customers <- F.getAllCustomers
_ <- customers.traverse_(L.logCustomer)
discount <- F.getDiscount(dt)
_ <- L.logDiscount(dt, discount)
updated <- customers.traverse(c =>
F.updateDiscountIfEligible(discount, c))
_ <- updated.traverse_(L.logCustomer)
} yield updated
A complex example
def isEligible(d: Discount, c: Customer): Boolean
def updateCustomer(d: Discount, c: Customer): Customer
case class ServiceState(customers: List[Customer],
discounts: Map[DiscountType, Discount])
A complex example
A complex example
val testInterp = new CustomerService[State[ServiceState, ?]] {
def getAllCustomers: State[ServiceState, List[Customer]] =
State.get[ServiceState].map(_.customers)
def getDiscount(dt: DiscountType): State[ServiceState, Discount] =
State.get[ServiceState].map(_.discounts(dt))
def updateDiscountIfEligible(d: Discount,
c: Customer): State[ServiceState, Customer] = …
}
A complex example
def updateDiscountIfEligible(d: Discount,
c: Customer): State[ServiceState, Customer] = State { s =>
if (isEligible(d, c)) {
val updated = updateCustomer(d, c)
val withoutCustomer = s.customers.filter(_ === c)
(s.copy(customers = updated +: withoutCustomer), updated)
} else {
(s, c)
}
}
A complex example
forAll { (customers: List[Customer], i: Item) =>
val d: Discount = Discount.Fix(0.2, List.empty)
val expected: Price = customers
.filter(c => isEligible(d, c)).foldMap(i.priceFor).discountBy(d)
val state: State[ServiceState, List[Customer]] =
discountAll(DiscountType.Fix)(testInterp, testLogger)
val initial = ServiceState(customers, Map(DiscountType.Fix -> d))
val price =
state.runA(ServiceState(customers)).value.foldMap(i.priceFor)
assert(price === expected)
}
Testing external effects - recap
● If the situation allows it, we can mock the behaviour of an external
service and therefore pull it into our world, making it fully deterministic
● We have to evaluate on a case by case basis if this feasible or worth
doing
● If done right, it can give us more confidence in our testing
Conclusions
● Separate effectful code from pure code
● Make use of total functions with well defined inputs as much as
possible
● When testing side-effects, see if you can mock some of the behaviour
Thank you for listening!
Twitter: @LukaJacobowitz
GitHub: LukaJCB

More Related Content

What's hot

One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
John De Goes
 
Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patterns
league
 
Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)stasimus
 
7 Habits For a More Functional Swift
7 Habits For a More Functional Swift7 Habits For a More Functional Swift
7 Habits For a More Functional Swift
Jason Larsen
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't Free
Kelley Robinson
 
Python programming : Standard Input and Output
Python programming : Standard Input and OutputPython programming : Standard Input and Output
Python programming : Standard Input and Output
Emertxe Information Technologies Pvt Ltd
 
Monad Transformers In The Wild
Monad Transformers In The WildMonad Transformers In The Wild
Monad Transformers In The WildStackMob Inc
 
Scalaz 8: A Whole New Game
Scalaz 8: A Whole New GameScalaz 8: A Whole New Game
Scalaz 8: A Whole New Game
John De Goes
 
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
John De Goes
 
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
 
Scala. Introduction to FP. Monads
Scala. Introduction to FP. MonadsScala. Introduction to FP. Monads
Scala. Introduction to FP. Monads
Kirill Kozlov
 
Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
Hang Zhao
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
John De Goes
 
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
 
First-Class Patterns
First-Class PatternsFirst-Class Patterns
First-Class Patterns
John De Goes
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator Pattern
Eric Torreborre
 
Fp in scala part 1
Fp in scala part 1Fp in scala part 1
Fp in scala part 1
Hang Zhao
 
Refactoring Functional Type Classes
Refactoring Functional Type ClassesRefactoring Functional Type Classes
Refactoring Functional Type Classes
John De Goes
 
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & AnalyticsQuark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
John De Goes
 
Hive function-cheat-sheet
Hive function-cheat-sheetHive function-cheat-sheet
Hive function-cheat-sheet
Dr. Volkan OBAN
 

What's hot (20)

One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
 
Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patterns
 
Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)
 
7 Habits For a More Functional Swift
7 Habits For a More Functional Swift7 Habits For a More Functional Swift
7 Habits For a More Functional Swift
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't Free
 
Python programming : Standard Input and Output
Python programming : Standard Input and OutputPython programming : Standard Input and Output
Python programming : Standard Input and Output
 
Monad Transformers In The Wild
Monad Transformers In The WildMonad Transformers In The Wild
Monad Transformers In The Wild
 
Scalaz 8: A Whole New Game
Scalaz 8: A Whole New GameScalaz 8: A Whole New Game
Scalaz 8: A Whole New Game
 
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
 
Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)
 
Scala. Introduction to FP. Monads
Scala. Introduction to FP. MonadsScala. Introduction to FP. Monads
Scala. Introduction to FP. Monads
 
Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
 
Map, Reduce and Filter in Swift
Map, Reduce and Filter in SwiftMap, Reduce and Filter in Swift
Map, Reduce and Filter in Swift
 
First-Class Patterns
First-Class PatternsFirst-Class Patterns
First-Class Patterns
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator Pattern
 
Fp in scala part 1
Fp in scala part 1Fp in scala part 1
Fp in scala part 1
 
Refactoring Functional Type Classes
Refactoring Functional Type ClassesRefactoring Functional Type Classes
Refactoring Functional Type Classes
 
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & AnalyticsQuark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
 
Hive function-cheat-sheet
Hive function-cheat-sheetHive function-cheat-sheet
Hive function-cheat-sheet
 

Similar to Testing in the World of Functional Programming

Writing DSL with Applicative Functors
Writing DSL with Applicative FunctorsWriting DSL with Applicative Functors
Writing DSL with Applicative Functors
David Galichet
 
Very basic functional design patterns
Very basic functional design patternsVery basic functional design patterns
Very basic functional design patterns
Tomasz Kowal
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
SeniorDevOnly
 
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
GeeksLab Odessa
 
Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)
Jonathan Felch
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
AOE
 
Joy of scala
Joy of scalaJoy of scala
Joy of scala
Maxim Novak
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
Wiem Zine Elabidine
 
Functions, Types, Programs and Effects
Functions, Types, Programs and EffectsFunctions, Types, Programs and Effects
Functions, Types, Programs and Effects
Raymond Roestenburg
 
Go Lang Tutorial
Go Lang TutorialGo Lang Tutorial
Go Lang Tutorial
Wei-Ning Huang
 
Scala Back to Basics: Type Classes
Scala Back to Basics: Type ClassesScala Back to Basics: Type Classes
Scala Back to Basics: Type Classes
Tomer Gabel
 
Who killed object oriented design?
Who killed object oriented design?Who killed object oriented design?
Who killed object oriented design?
Amir Barylko
 
User Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love StoryUser Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love Story
Databricks
 
User Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love StoryUser Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love Story
Databricks
 
Improving Correctness with Types Kats Conf
Improving Correctness with Types Kats ConfImproving Correctness with Types Kats Conf
Improving Correctness with Types Kats Conf
Iain Hull
 
Coding in Style
Coding in StyleCoding in Style
Coding in Style
scalaconfjp
 
Finagle and Java Service Framework at Pinterest
Finagle and Java Service Framework at PinterestFinagle and Java Service Framework at Pinterest
Finagle and Java Service Framework at Pinterest
Pavan Chitumalla
 
李建忠、侯捷设计模式讲义
李建忠、侯捷设计模式讲义李建忠、侯捷设计模式讲义
李建忠、侯捷设计模式讲义yiditushe
 
From OOP To FP Through A Practical Case
From OOP To FP Through A Practical CaseFrom OOP To FP Through A Practical Case
From OOP To FP Through A Practical Case
Cristina Delgado Rodríguez
 

Similar to Testing in the World of Functional Programming (20)

Writing DSL with Applicative Functors
Writing DSL with Applicative FunctorsWriting DSL with Applicative Functors
Writing DSL with Applicative Functors
 
Very basic functional design patterns
Very basic functional design patternsVery basic functional design patterns
Very basic functional design patterns
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
 
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
 
Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)
 
Day 1
Day 1Day 1
Day 1
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
 
Joy of scala
Joy of scalaJoy of scala
Joy of scala
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
Functions, Types, Programs and Effects
Functions, Types, Programs and EffectsFunctions, Types, Programs and Effects
Functions, Types, Programs and Effects
 
Go Lang Tutorial
Go Lang TutorialGo Lang Tutorial
Go Lang Tutorial
 
Scala Back to Basics: Type Classes
Scala Back to Basics: Type ClassesScala Back to Basics: Type Classes
Scala Back to Basics: Type Classes
 
Who killed object oriented design?
Who killed object oriented design?Who killed object oriented design?
Who killed object oriented design?
 
User Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love StoryUser Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love Story
 
User Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love StoryUser Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love Story
 
Improving Correctness with Types Kats Conf
Improving Correctness with Types Kats ConfImproving Correctness with Types Kats Conf
Improving Correctness with Types Kats Conf
 
Coding in Style
Coding in StyleCoding in Style
Coding in Style
 
Finagle and Java Service Framework at Pinterest
Finagle and Java Service Framework at PinterestFinagle and Java Service Framework at Pinterest
Finagle and Java Service Framework at Pinterest
 
李建忠、侯捷设计模式讲义
李建忠、侯捷设计模式讲义李建忠、侯捷设计模式讲义
李建忠、侯捷设计模式讲义
 
From OOP To FP Through A Practical Case
From OOP To FP Through A Practical CaseFrom OOP To FP Through A Practical Case
From OOP To FP Through A Practical Case
 

More from Luka Jacobowitz

Up and Running with the Typelevel Stack
Up and Running with the Typelevel StackUp and Running with the Typelevel Stack
Up and Running with the Typelevel Stack
Luka Jacobowitz
 
Principled Error Handling - Scalapeño
Principled Error Handling - ScalapeñoPrincipled Error Handling - Scalapeño
Principled Error Handling - Scalapeño
Luka Jacobowitz
 
Building a Tagless Final DSL for WebGL
Building a Tagless Final DSL for WebGLBuilding a Tagless Final DSL for WebGL
Building a Tagless Final DSL for WebGL
Luka Jacobowitz
 
What Referential Transparency can do for you
What Referential Transparency can do for youWhat Referential Transparency can do for you
What Referential Transparency can do for you
Luka Jacobowitz
 
Scala UA 2017
Scala UA 2017Scala UA 2017
Scala UA 2017
Luka Jacobowitz
 
Reactive Programming in the Browser feat. Scala.js and Rx
Reactive Programming in the Browser feat. Scala.js and RxReactive Programming in the Browser feat. Scala.js and Rx
Reactive Programming in the Browser feat. Scala.js and Rx
Luka Jacobowitz
 
Reactive Programming in the Browser feat. Scala.js and PureScript
Reactive Programming in the Browser feat. Scala.js and PureScriptReactive Programming in the Browser feat. Scala.js and PureScript
Reactive Programming in the Browser feat. Scala.js and PureScript
Luka Jacobowitz
 

More from Luka Jacobowitz (7)

Up and Running with the Typelevel Stack
Up and Running with the Typelevel StackUp and Running with the Typelevel Stack
Up and Running with the Typelevel Stack
 
Principled Error Handling - Scalapeño
Principled Error Handling - ScalapeñoPrincipled Error Handling - Scalapeño
Principled Error Handling - Scalapeño
 
Building a Tagless Final DSL for WebGL
Building a Tagless Final DSL for WebGLBuilding a Tagless Final DSL for WebGL
Building a Tagless Final DSL for WebGL
 
What Referential Transparency can do for you
What Referential Transparency can do for youWhat Referential Transparency can do for you
What Referential Transparency can do for you
 
Scala UA 2017
Scala UA 2017Scala UA 2017
Scala UA 2017
 
Reactive Programming in the Browser feat. Scala.js and Rx
Reactive Programming in the Browser feat. Scala.js and RxReactive Programming in the Browser feat. Scala.js and Rx
Reactive Programming in the Browser feat. Scala.js and Rx
 
Reactive Programming in the Browser feat. Scala.js and PureScript
Reactive Programming in the Browser feat. Scala.js and PureScriptReactive Programming in the Browser feat. Scala.js and PureScript
Reactive Programming in the Browser feat. Scala.js and PureScript
 

Recently uploaded

Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Mind IT Systems
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
Globus
 
RISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent EnterpriseRISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent Enterprise
Srikant77
 
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus
 
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume MontevideoVitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke
 
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
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Globus
 
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdfDominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
AMB-Review
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
abdulrafaychaudhry
 
Corporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMSCorporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMS
Tendenci - The Open Source AMS (Association Management Software)
 
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
 
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
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar
 
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdfEnhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Jay Das
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
Globus
 
Graphic Design Crash Course for beginners
Graphic Design Crash Course for beginnersGraphic Design Crash Course for beginners
Graphic Design Crash Course for beginners
e20449
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
Fermin Galan
 
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
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
vrstrong314
 
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
 

Recently uploaded (20)

Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
 
RISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent EnterpriseRISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent Enterprise
 
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024
 
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume MontevideoVitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume Montevideo
 
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
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
 
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdfDominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
 
Corporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMSCorporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMS
 
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...
 
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
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdfEnhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
 
Graphic Design Crash Course for beginners
Graphic Design Crash Course for beginnersGraphic Design Crash Course for beginners
Graphic Design Crash Course for beginners
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
 
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
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
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
 

Testing in the World of Functional Programming

  • 1. Testing in the world of FP Luka Jacobowitz - Lamdba World 2018
  • 2. Software Developer at codecentric Co-organizer of ScalaDus and IdrisDus Maintainer of cats, cats-effect, cats-mtl, cats-tagless, OutWatch Enthusiastic about FP About me
  • 3. Agenda ● Property-based testing ● Mocking ● Conclusions
  • 4. Property-based testing is awesome ● It allows us to generate a bunch of test cases we would never have been able to write by ourselves ● Makes sure even edge cases are well handled ● Can help us find bugs as early as possible
  • 5. Property-based testing - Pains /** * Should use UUIDv1 and "yyyy-MM-dd HH:mm:ss" format */ def uuidCreatedAfter(uuid: String, date: String): Boolean = { val arr = uuid.split("-") val timestampHex = arr(2).substring(1) + arr(1) + arr(0) ... }
  • 6. Newtype it! Common reasons against newtyping: ● It won’t support the operations I need ● It will lead to a lot of boilerplate conversions ● It will give us a performance penalty
  • 7. scala-newtype @newtype case class Euros(n: Int) object Euros { implicit val eurosNumeric: Numeric[Euros] = deriving } Euros(25) + Euros(10) - Euros(5)
  • 8. Refined val url: String Refined Url = "http://example.com" val failed: String Refined Url = "hrrp://example.com" error: Url predicate failed: unknown protocol: hrrp val n: Int Refined Positive = 42
  • 9. Refined-Scalacheck import eu.timepit.refined.scalacheck.numeric._ def leftPad(s: String, n: Int Refined Positive): String forAll { (s: String, n: Int Refined Positive) => assert(leftPad(s, n).length >= n) }
  • 10. Accepting only valid inputs for your functions makes your code more precise and allows for much easier property-based testing
  • 11. So many more gems out there val n: Int Refined Positive = NonEmptyList.of(1, 2, 3).refinedSize val v: ValidatedNel[String, SHA1] = SHA1.validate("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")
  • 12. Use this instead ● A duration of time -> use FiniteDuration instead of Int ● A non-empty sequence -> use NonEmptyList instead of List ● A date -> use LocalDate instead of String ● An IP-address -> use String Refined IPv4 instead of String ● etc.
  • 13. Other great libraries ● Libra - A dimensional analysis library, allows us to multiply two meter values and receive a square meter value ● FUUID - Functional UUID library, gives us new random UUIDs in IO and uses Either for construction from String ● Squants - Similar to Libra, but also comes with built in units of measure such as KiloWatts, Tons and even Money
  • 14. How do I test this? def sendXml(s: String): Unit def sendAll(list: List[String]): Unit = { list.foreach(sendXml) }
  • 15. Split it up def parseXml(s: String): Either[Throwable, Xml] def sendXml(x: Xml): IO[Unit] def parseAndSend(s: String): IO[Unit] = IO.fromEither(parseXml(s)).flatMap(sendXml) list.traverse_(parseAndSend)
  • 16. Now we can test parsing, what about sending? def sendXml(x: Xml): IO[Unit]
  • 17. Testing what we don’t control We have two (non mutually exclusive) options 1. Use integration tests to check if the behaviour does what we want (can be difficult) 2. Mock the outside world and test expectations (easier, but potentially less accurate)
  • 18. How do we use mocking to test IO? Tagless Final
  • 19. Tagless Final ● Allows us to separate problem description from the actual problem solution and implementation ● This means we can use our own Algebras for defining interactions ● We can work at an extra level of abstraction but maintain flexibility
  • 20. Tagless Final - How to ● Model our Algebras as traits parametrized with a type constructor ● Programs constrain the type parameter (e.g. with Monad) ● Interpreters are simply implementations of those traits
  • 21. An example def bookThings: IO[Unit] = for { _ <- bookDrink(coke) _ <- bookRoomService _ <- bookSandwich(wholeWheat, hummus) } yield ()
  • 22. Using Tagless Final trait BookingService[F[_]] { def bookDrink(b: Beverage): F[Unit] def bookRoomService: F[Unit] def bookSandwich(d: Dough, t: Topping): F[Unit] } def bookThings[F[_]: Apply](F: BookingService[F]): F[Unit] = F.bookDrink(coke) *> F.bookRoomService *> F.bookSandwich(wholeWheat, hummus)
  • 23. A possible Test interpreter sealed trait Booking case class DrinkBooking(b: Beverage) extends Booking case object RoomServiceBooking extends Booking case class SandwichBooking(d: Dough, t: Topping) extends Booking def testService = new BookingService[Const[List[Booking], ?]] { def bookDrink(b: Beverage): Const[List[Booking], Unit] = Const(List(DrinkBooking(b))) def bookRoomService: Const[List[Booking], Unit] = Const(List(RoomServiceBooking)) def bookSandwich(d: Dough, t: Topping): Const[List[Booking], Unit] = Const(List(SandwichBooking(d, t))) }
  • 24. Running the program val bookings: List[Booking] = bookThings(testService).getConst val expectedBookings: List[Booking] = List(...) assert(bookings === expectedBookings) What are we testing here? The external system? We’re only testing the interal wiring of our own system.
  • 25. External world testing continued Last time was too easy, we were just returning Unit! Usually we have to deal with more complex return types that are much harder to mock.
  • 26. External world testing continued Two questions: 1. What does your external service provide to you, is it just data or also State? 2. Can you reasonably mimic the behaviour of the external service with a self-contained state machine?
  • 27. A complex example def discountAll(dt: DiscountType): IO[List[Customer]] = for { customers <- getAllCustomers _ <- customers.traverse_(logCustomer) discount <- getDiscount(dt) _ <- logDiscount(dt, discount) updated <- customers.traverse(c => updateDiscountIfEligible(discount, c)) _ <- updated.traverse_(logCustomer) } yield updated
  • 28. A complex example trait Logging[F[_]] { def logCustomer(c: Customer): F[Unit] def logDiscount(dt: DiscountType, d: Discount): F[Unit] }
  • 29. A complex example trait CustomerService[F[_]] { def getAllCustomers: F[List[Customer]] def getDiscount(dt: DiscountType): F[Discount] def updateDiscountIfEligible(d: Discount, c: Customer): F[Customer] }
  • 30. def discountAll[F[_]: Monad](dt: DiscountType) (F: CustomerService[F], L: Logging[F]): F[List[Customer]] = for { customers <- F.getAllCustomers _ <- customers.traverse_(L.logCustomer) discount <- F.getDiscount(dt) _ <- L.logDiscount(dt, discount) updated <- customers.traverse(c => F.updateDiscountIfEligible(discount, c)) _ <- updated.traverse_(L.logCustomer) } yield updated A complex example
  • 31. def isEligible(d: Discount, c: Customer): Boolean def updateCustomer(d: Discount, c: Customer): Customer case class ServiceState(customers: List[Customer], discounts: Map[DiscountType, Discount]) A complex example
  • 32. A complex example val testInterp = new CustomerService[State[ServiceState, ?]] { def getAllCustomers: State[ServiceState, List[Customer]] = State.get[ServiceState].map(_.customers) def getDiscount(dt: DiscountType): State[ServiceState, Discount] = State.get[ServiceState].map(_.discounts(dt)) def updateDiscountIfEligible(d: Discount, c: Customer): State[ServiceState, Customer] = … }
  • 33. A complex example def updateDiscountIfEligible(d: Discount, c: Customer): State[ServiceState, Customer] = State { s => if (isEligible(d, c)) { val updated = updateCustomer(d, c) val withoutCustomer = s.customers.filter(_ === c) (s.copy(customers = updated +: withoutCustomer), updated) } else { (s, c) } }
  • 34. A complex example forAll { (customers: List[Customer], i: Item) => val d: Discount = Discount.Fix(0.2, List.empty) val expected: Price = customers .filter(c => isEligible(d, c)).foldMap(i.priceFor).discountBy(d) val state: State[ServiceState, List[Customer]] = discountAll(DiscountType.Fix)(testInterp, testLogger) val initial = ServiceState(customers, Map(DiscountType.Fix -> d)) val price = state.runA(ServiceState(customers)).value.foldMap(i.priceFor) assert(price === expected) }
  • 35. Testing external effects - recap ● If the situation allows it, we can mock the behaviour of an external service and therefore pull it into our world, making it fully deterministic ● We have to evaluate on a case by case basis if this feasible or worth doing ● If done right, it can give us more confidence in our testing
  • 36. Conclusions ● Separate effectful code from pure code ● Make use of total functions with well defined inputs as much as possible ● When testing side-effects, see if you can mock some of the behaviour
  • 37. Thank you for listening! Twitter: @LukaJacobowitz GitHub: LukaJCB