How Scala promotes TDD

31
How Scala promotes TDD How Scala allows you to write better and more testable code audience.filter(_.usesJava).foreach { member => sayHi(member) }

description

This talk demonstrates why Scala is in my opinion the best language to do TDD on

Transcript of How Scala promotes TDD

Page 1: How Scala promotes TDD

How Scala promotes TDD

How Scala allows you to write better and

more testable code

audience.filter(_.usesJava).foreach { member =>

sayHi(member)

}

Page 2: How Scala promotes TDD

À La Carte

ApéritifTDD and programing languages

EntréeA short overview of Scala’s features

Plat Principal• Better value objects using Case Classes • Determinism via immutability

Page 3: How Scala promotes TDD

À La Carte

Plat Principal (cont’d)• Better type safety, no nulls and less exception

throwing • Better composition and cross-cutting concerns

using Traits• Declarative asynchrony

Le Dessert• Specs2• ScalaTest• Plain old JUnit• Mocking

Page 4: How Scala promotes TDD

Apéritif

Page 5: How Scala promotes TDD

TDD and programing languages

There’s a set of programming language features that are

necessary in order to grow software using TDD. These

include referential transparency, well-defined types that

are easy to declare, the ability to separate concerns into

individual codes of block and, of course, providing the

means to write short, concise and clear tests.

Page 6: How Scala promotes TDD

Entrée

Page 7: How Scala promotes TDD

A short overview of Scala’s features

• A functional/OO programming language that runs on

the JVM

• Everything is an object – no primitive types

• Functions are first-class members, every function is a

value, including what is usually an operator

• Scala is statically-typed and supports type inference

Page 8: How Scala promotes TDD

A short overview of Scala’s features

• Lambda expressions, closures and currying naturally

• Pattern matching

• Multiple inheritance through Traits

• Scala is extensible, allowing you to write your own

“language structures”

Page 9: How Scala promotes TDD

Plat Principal

Page 10: How Scala promotes TDD

Case Classes

Good software engineering makes use of value objects.

These need to encapsulate the way they represent their

state, to provide information hiding and to be easy to

maintain.

case class Cat(name: String, age: Int, kittens: Seq[Cat] = Nil)

val philip = Cat(name = “Philip”, age = 9)

val aKitten = Cat(age = 1, name = “Shraga”)val withKittens = philip.copy(kittens = Seq(aKitten))

Page 11: How Scala promotes TDD

Determinism via Immutability

Scala encourages everything to be immutable by default:

• Variables (vals)

• Collections

• Value objects (using Case Classes)

• Composition of collaborators

Page 12: How Scala promotes TDD

Determinism via ImmutabilityAs a result, we get rid of annoying problems such as

aliasing, concurrent modifications of collections and

objects that have an unknown state

case class Cat(kittens: Seq[Cat] = Nil, dirty: Boolean = true) extends Pet

class Clinic(val shower: PetShower) { def wash(cat: Cat): Cat = { val kittens = cat.kittens.map shower.wash shower.wash(cat.copy(kittens = kittens) }}

Page 13: How Scala promotes TDD

Determinism via ImmutabilityTesting Clinic.wash() should prove quite simple

val shower = mock[PetShower]val clinic = new Clinic(shower)

val kitten1 = Cat(dirty = true)val kitten2 = Cat(dirty = true)val mom = Cat(kittens = Seq(kitten1, kitten2)

clinic.wash(mom)

verify(shower).wash(kitten1)verify(shower).wash(kitten2)verify(shower).wash(mom)

Page 14: How Scala promotes TDD

Better type safety

• Scala is statically and strongly typed; type inference

keeps the code lean and mean

• Stricter generics (in comparison to Java) provide better

compile-time checks

• Advanced features include structural types and type

aliases

Page 15: How Scala promotes TDD

No nulls

Scala urges us to declare a possible return value using the

Option[T] monad; an option can be either Some(value) or

None, and allows us to assume to it can never be null. We

can use collection semantics to consume an Option[T].

def foo: Option[Foo]

foo match { case Some(Foo(bar)) => println(bar) case _ => println(“No foo found”)}

val barOrDefault = foo.map(_.bar).getOrElse(“no foo”)

Page 16: How Scala promotes TDD

Less exception throwing using Try[T]

• Try[T] is an abstract monad type with two concrete

implementations, Success[T] and Failure[E]

• Represents the result of an operation which may fail

• Automatically translate an exception-throwing clause

to a Try[T] using the Try() apply method

Page 17: How Scala promotes TDD

Less exception throwingclass SomeJavaObject { public Bar tryFoo() throws FooException {…}}

val someJavaObject = new SomeJavaObjectval maybeBar = Try(someJavaObject.tryFoo())

maybeBar match { case Success(bar) => println(bar) case _ => reportFailureAndRetry() }

Page 18: How Scala promotes TDD

Better composition using Traits

Scala provides the means for multiple inheritance using

Traits; this can be useful for separating related but

independent pieces of logic into separate units of code,

each with its own test suite. class ComponentFactory extends ImageCreation with TextCreation with VideoCreation with … {

def create(cd: ComponentDefinition) = cd match { case Image(url, dimensions) => makeImage(…) case Text(text, kind) => makeText(…) … }}

Page 19: How Scala promotes TDD

Better composition using Traitstrait ImageCreation { def makeImage(url: String, dimensions: Dimensions) = {…} }

class ImageCreationTest extends SpecificationWithJUnit {

val creator = new ImageCreation {… // init code}

“makeImage” should { “create an image” in {…} “fail gracefully” in {…} }}

Page 20: How Scala promotes TDD

Cross-cutting concerns using TraitsIn the Java world, AOP can be used to add cross-cutting

concerns to existing code without altering it, but has the

downside of being non-transparent or too implicit. This

makes it hard to figure out which aspects are applied at

runtime, and impossible to test that aspects are indeed

being applied properly.

Let’s look at an example using the canonical use case for

AOP – auditing.

Page 21: How Scala promotes TDD

Cross-cutting concerns using Traitstrait Auditing { def auditor: Auditor def audited(f: () => T): T = { auditor.before(…) val ret: T = f() auditor.after(…) ret }}

class Foo(baz: Baz, val auditor: Auditor) extends Auditing { def bar() { audited { baz.doSomething() } }}

Page 22: How Scala promotes TDD

Cross-cutting concerns using Traitsclass FooTest extends SpecificationWithJUnit { val auditor = mock[Auditor] val baz = mock[Baz] val foo = new Foo(baz, auditor)

“Foo” should { “call baz” in { foo.bar() got { one(baz).doSomething() one(auditor).audit(…) } } }}

Page 23: How Scala promotes TDD

Declarative asynchrony

Scala 2.10 adds a top notch Promise/Future library with

support for composing and pipelining, using callback or

monadic semantics.

A Future[T] will never throw an exception, it will return a

Try[T].

Page 24: How Scala promotes TDD

Declarative asynchronytrait CatVaccinator{ def vaccinate(cat: Cat): Future[VaccinatedCat]}

trait PetStore { def deliver(cats: Seq[VaccinatedCat]): Unit}

class Vet(vaccinator: CatVaccinator, petStore: PetStore){ def deliver(cats: Seq[Cat]) { Future.sequence(cats.map vaccinator.vaccinate) .onSuccess { vaccinatedCats: Seq[VaccinatedCat] => petStore.deliver(vaccinatedCats) } .onFailure { exception => reportAndRetry(cats) // some retry logic } }}

Page 25: How Scala promotes TDD

Declarative asynchrony// SameThreadExecutor or DeterministicExecutorval executorService: ExecutorService = …val vaccinator = mock[CatVaccinator]val petStore = mock[PetStore]val vet = new Vet(vaccinator, petStore)

“Vet” should { “call deliver” in { vaccinator.vaccinate(cat1) returns Future(vcat1) vaccinator.vaccinate(cat2) returns Future(vcat2)

vet.deliver(Seq(cat1, cat2))

got { one(petStore).deliver(Seq(vcat1, vcat2)) } }}

Page 26: How Scala promotes TDD

Le Dessert

Page 27: How Scala promotes TDD

Specs2

• Is somewhat of a de-facto standard in the Scala

community

• Github, active community, frequent releases

• Support for RSpec-style and BDD-style test code

• Rich matcher library

• Mediocre documentation

• Built-in Hamcrest integration

Page 28: How Scala promotes TDD

ScalaTest

• Somewhat behind Specs2 in terms of adoption

• Supports a myriad of test formats (RSpec, BDD, XUnit,

etc)

• Rich and reliable documentation

• Poor matcher library

• No built-in Hamcrest integration

Page 29: How Scala promotes TDD

Plain-old JUnit

• Lots of boilerplate

• Hamcrest doesn’t play well with Scala (for instance, for

matching collections)

• Less magic in comparison with Specs2 and ScalaTest

• No namespace collisions and easier to debug if

something weird happens

Page 30: How Scala promotes TDD

Mocking

• ScalaTest supports ScalaMock, Mockito, JMock and

EasyMock

• Specs2 only supports Mockito out of the box but

writing your own sugar using Mixin traits is easy

• ScalaMock is a native Scala mock objects library. Worth

adopting if you don’t already rely heavily on another

library