# Tutorial

## Tutorial

## Thanks

**This guide was originally copied from the very excellent
ScalaCheck Guide
and repurposed for Hedgehog**.

## What is Hedgehog?

Hedgehog is a tool for testing Scala and Java programs, based on property specifications and automatic test data generation. The basic idea is that you define a property that specifies the behaviour of a method or some unit of code, and Hedgehog checks that the property holds. All test data are generated automatically in a random fashion, so you don't have to worry about any missed cases.

## Getting started

Please follow the general getting started guide first.

### A quick example

`import hedgehog._`

import hedgehog.runner._

object Spec extends Properties {

override def tests: List[Test] =

List(

property("property", propConcatLists)

)

def propConcatLists: Property =

for {

l1 <- Gen.int(Range.linear(-100, 100)).list(Range.linear(0, 100)).forAll

l2 <- Gen.int(Range.linear(-100, 100)).list(Range.linear(0, 100)).forAll

} yield l1.size + l2.size ==== (l1 ::: l2).size

}

You can run this from `sbt`

, either via `test`

or an application with `run`

.

`scala> test`

+ Spec$.property: OK, passed 100 tests

scala> run Spec

+ Spec$.property: OK, passed 100 tests

OK, that seemed alright. Now define another property.

`def propSqrt: Property =`

for {

n <- Gen.int(Range.linearFrom(0, -100, 100)).forAll

} yield scala.math.sqrt(n * n) ==== n

Check it!

`- Spec$.property: Falsified after 8 passed tests`

> -1

> === Not Equal ===

> --- lhs ---

> 1.0

> --- rhs ---

> -1.0

Not surprisingly, the property doesn't hold. The argument `-1`

falsifies it.

### Just a library

Before we continue it's worth pointing out that for the most past Hedgehog is
*just* a library. Let's run our first property directly using the API.

`scala> import hedgehog._`

scala> Property.checkRandom(Spec.propConcatLists).value

res0: hedgehog.core.Report = Report(SuccessCount(100),DiscardCount(0),OK)

We can see that the test output is returned as pure data.

Feel free to run the properties in this guide in any way you find most convenient. We will continue to display the results from "running" the property, just because it's more readable.

## Properties

There are two main concepts to Hedgehog are:

Let's start with the more simple and familar results and then move on to the more interesting generators.

### Results

A `Result`

is really a simple `Boolean`

assertion with extra logging.
That's it (no really).

`def testAdd: Result =`

Result.assert(1 + 2 == 2 + 1)

You can actually run these from Hedgehog like you would a full property.

`object Spec extends Properties {`

override def tests: List[Test] =

List(

example("add", testAdd)

)

def testAdd: Result =

Result.assert(1 + 2 == 2 + 1)

}

Note that we've used the `example`

test function here instead of `property`

,
which is used for the more powerful `Property`

result.

And when we test it, notice that it only runs once and not 100 times.

`+ Spec$.add: OK, passed 1 tests`

This is just like any other test in ScalaTest/specs2/junit/etc.

What happens if we fail though?

`def testAdd: Result =`

Result.assert(1 + 2 == 3 + 4)

`Spec$.add: Falsified after 1 passed tests`

That's it? What about a useful message telling us what failed?
For starters, given that we're just doing an assertion Hedgehog comes with the
convenient `====`

(quadruple `=`

s) operator:

`def testAdd: Result =`

1 + 2 ==== 3 + 4

`Spec$.testAdd: Falsified after 1 passed tests`

> === Not Equal ===

> --- lhs ---

> 3

> --- rhs ---

> 7

That's a little better. But what happens if we don't just want to check equality?

There is a method called `diff`

which is similar to `Result.assert`

but it gives the similar message to `====`

operator's.

`diff`

takes two arguments and the comparison function so that you can do any comparison operation you want on those two arguments.

`def testAdd: Result =`

Result.diff(1 + 2, 3 + 4)(_ == _)

`Spec$.testAdd: Falsified after 1 passed tests`

> === Failed ===

> --- lhs ---

> 3

> --- rhs ---

> 7

`def a1GtA2: Result =`

Result.diff(1 + 2, 3 + 4)(_ > _)

`Spec$.a1GtA2: Falsified after 0 passed tests`

> === Failed ===

> --- lhs ---

> 3

> --- rhs ---

> 7

If you want to change the log name (i.e. `=== Failed ===`

) to something else, you can use `diffNamed`

instead.

e.g.)

`Result.diffNamed("=== Not Equal ===", 1 + 2, 3 + 4)(_ == _)`

`Spec$.testAdd: Falsified after 1 passed tests`

> === Not Equal ===

> --- lhs ---

> 3

> --- rhs ---

> 7

In fact, `====`

internally uses the `diffNamed`

method.

#### Logging

Sometimes it can be difficult to decide exactly what is wrong when a property fails, especially if the property is complex, with many conditions. In such cases, you can log the different parts of the property, so Hedgehog can tell you exactly what part is failing.

From the the example above, what happens if we wanted to check if two numbers were less than each other?

`def testTL: Result =`

Result.assert(2 < 1)

As we saw earlier, this wouldn't give a very useful error message.
This is where `log`

comes in handy.

`def testTL: Result =`

Result.assert(2 < 1)

.log("2 is not less than 1")

We could ever make our own function to help re-use this.

`def isLessThan(a: Int, b: Int): Result =`

Result.assert(a < b)

.log(s"$a is not less than $b")

def testTL: Result =

isLessThan(2, 1)

Where logging really comes in handy is when you start to use generators and the results are different every time.

`val complexProp: Property =`

for {

m <- Gen.int(Range.linear(1, 100)).log("m")

n <- Gen.int(Range.linear(1, 100)).log("n")

} yield {

val res = m + n

Result.all(List(

Result.assert(res >= m).log("result > #1")

, Result.assert(res >= n).log("result > #2")

, Result.assert(res < m + n).log("result not sum")

))

}

`- Spec.property: Falsified after 0 passed tests.`

> m: 0

> n: 0

> result not sum

The log operator can also be used to inspect intermediate values used in the properties, which can be very useful when trying to understand why a property fails. Hedgehog always presents the generated property arguments but sometimes you need to quickly see the value of an intermediate calculation. See the following example, which tries to specify multiplication in a somewhat naive way:

`def propMul: Property =`

for {

n <- Gen.int(Range.linear(1, 100)).log("n")

m <- Gen.int(Range.linear(1, 100)).log("m")

} yield {

val res = n*m

Result.all(List(

(res / m ==== n).log("div1")

, (res / n ==== m).log("div2")

, Result.assert(res > m).log("lt1")

, Result.assert(res > n).log("lt2")

)).log("evidence = " + res)

}

Here we have four different conditions, each with its own label. A fifth label is added to the combined property to record the result of the multiplication. When we check the property, Hedgehog tells us the following:

`- Spec$.example: Falsified after 0 passed tests.`

> n: 1

> n: 1

> lt1

> lt2

> evidence = 1

As you can see, you can add as many logs as you want to your result, Hedgehog will only present the failing ones for the smallest example.

#### Combining

Results can be combined with other results into new ones using familiar boolean logic.

`def p1: Result =`

"a" ==== "a"

def p2: Result =

1 ==== 1

def p3: Result =

p1 and p2

def p4: Result =

p1 or p2

// same as p1 and p2

def p5: Result =

Result.all(List(p1, p2))

// same as p1 or p2

def p6: Result =

Result.any(List(p1, p2))

Here, `p3`

will hold if and only if both `p1`

and `p2`

hold, `p4`

will hold if
either `p1`

or `p2`

holds.

### Generators

Generators are responsible for generating test data in Hedgehog, and are
represented by the `hedgehog.Gen`

class. You need to know how to use this
class if you want Hedgehog to generate data of types that are not supported
by default to state properties about a specific subset of a type. In the `Gen`

object, there are several methods for creating new and modifying existing
generators. We will show how to use some of them in this section. For a more
complete reference of what is available, please see the Github source.

A generator can be seen simply as a function that takes some generation
parameters, and (maybe) returns a generated value. That is, the type `Gen[T]`

may be thought of as a function of type `Seed => Option[T]`

. However, the
`Gen`

class contains additional methods to make it possible to map generators,
use them in for-comprehensions and so on. Conceptually, though, you should
think of generators simply as functions, and the combinators in the `Gen`

object can be used to create or modify the behaviour of such generator
functions.

Let's see how to create a new generator. The best way to do it is to use the
generator combinators that exist in the `hedgehoge.Gen`

module. These can
be combined using a for-comprehension. Suppose you need a generator which
generates a tuple that contains two random integer values, one of them being at
least twice as big as the other. The following definition does this:

`val myGen: Gen[(Int, Int)] =`

for {

n <- Gen.int(Range.linear(10, 20))

m <- Gen.int(Range.linear(2*n, 500))

} yield (n, m)

You can create generators that picks one value out of a selection of values. The following generator generates a vowel:

`def vowel: Gen[Char] =`

Gen.element1('A', 'E', 'I', 'O', 'U', 'Y')

The `element1`

method creates a generator that randomly picks one of its
parameters each time it generates a value. Notice that plain values are
implicitly converted to generators (which always generates that value) if
needed.

The distribution is uniform, but if you want to control it you can use the
`frequency1`

combinator:

`def vowel: Gen[Char] =`

Gen.frequency1(

(3, 'A')

, (4, 'E')

, (2, 'I')

, (3, 'O')

, (1, 'U')

, (1, 'Y')

)

Now, the `vowel`

generator will generate `E`

s more often than `Y`

s. Roughly, 4/14
of the values generated will be `E`

s, and 1/14 of them will be `Y`

s.

#### Case Classes

It is very simple to generate random instances of case classes in Hedgehog. Consider the following example where a binary integer tree is generated:

`sealed abstract class Tree`

case class Node(left: Tree, right: Tree, v: Int) extends Tree

case object Leaf extends Tree

val genLeaf: Gen[Tree] =

Gen.constant(Leaf)

def genNode: Gen[Tree] =

for {

v <- Gen.int(Range.linear(-100, 100))

left <- genTree

right <- genTree

} yield Node(left, right, v)

def genTree: Gen[Tree] =

Gen.choice1(genLeaf, genNode)

We can now generate a sample tree:

`def testTree: Property =`

forAll {

t <- genTree.forAll

} yield {

println(t)

Result.success

}

`Leaf`

Node(Leaf,Leaf,-71)

Node(Node(Leaf,Leaf,-71),Node(Leaf,Leaf,-49),17),Leaf,-20

Node(Node(Node(Node(Node(Leaf,Leaf,-71),Node(Leaf,Leaf,-49),17),Leaf,-20),Leaf,-7),Node(Node(Leaf,Leaf,26),Leaf,-3),49)

Node(Leaf,Node(Node(Node(Node(Node(Node(Leaf,Leaf,-71),Node(Leaf,Leaf,-49),17),Leaf,-20),Leaf,-7),Node(Node(Leaf,Leaf,26),Leaf,-3),49),Leaf,84),-29)

#### Lists

There is a use generator, `list`

, that generates a list of the current `Gen`

.
You can use it in the following way:

`def genIntList: Gen[List[Int]] =`

Gen.element1(1, 3, 5).list(Range.linear(0, 10))

def genBoolList: Gen[List[Boolean]] =

Gen.constant(true).list(Range.linear(0, 10))

def genCharList: Gen[List[Char]] =

Gen.alpha.list(Range.linear(0, 10))

It might be annoying to deal with a list of characters, which is where the
`Gen.string`

function comes in handy.

`def genStringList: Gen[String] =`

Gen.string(Gen.alpha, Range.linear(0, 10))

#### Filtering

Generator values can be restricted to ensure they meet some precondition.

`val propMakeList: Property =`

for {

n <- Gen.int(Range.linear(0, 100))

.ensure(n => n % 2 == 0)

.forAll

} yield List.fill(n)("").length ==== n

}

Now Hedgehog will only care for the cases when `n`

is even.

If `ensure`

is given a condition that is hard or impossible to
fulfill, Hedgehog might not find enough passing test cases to state that the
property holds. In the following trivial example, all cases where `n`

is
non-zero will be thrown away:

`def propTrivial: Property =`

for {

n <- Gen.int(Range.linear(0, 100))

.ensure(n => n == 0)

.forAll

} yield n ==== 0

`> Gave up after only 55 passed tests. 100 were discarded`

It is possible to tell Hedgehog to try harder when it generates test cases, but generally you should try to refactor your property specification instead of generating more test cases, if you get this scenario.

Using `ensure`

, we realise that a property might not just pass or fail, it
could also be undecided if the implication condition doesn't get fulfilled.

#### Sized

When Hedgehog uses a generator to generate a value, it feeds it with some
parameters. One of the parameters the generator is given, is a `Size`

value,
which some generators use to generate their values. If you want to use the size
parameter in your own generator, you can use the `Gen.sized`

method:

`def matrix[T](g: Gen[T]): Gen[List[List[T]]] =`

Gen.sized(size => {

val side = scala.math.sqrt(size.value).toInt

g.list(Range.linear(0, side)).list(Range.linear(0, side))

})

The `matrix`

generator will use a given generator and create a matrix which
side is based on the generator size parameter. It uses the `list`

function
which creates a sequence of given length filled with values obtained from the
given generator.

### Shrinking

In some ways the most interesting and important feature of Hedgehog is that if
it finds an argument that falsifies a property, it tries to *shrink* that
argument before it is reported.

This is done automatically! This is crucially different from [QuickCheck] and [ScalaCheck] which requires some hand-holding when it comes to shrinking. We recommended watching the original presentation for more information on how this works.

Let's look at specifying a property that says that no list has duplicate elements in it. This is of course not true, but we want to see the test case shrinking in action!

`def p1: Property =`

for {

l <- Gen.int(Range.linearFrom(0, -100, 100)).list(Range.linear(0, 100)).log("l")

} yield l ==== l.distinct

Now, run the tests:

`- Spec$.example: Falsified after 5 passed tests`

> l: List(0,0)

> === Not Equal ===

> --- lhs ---

> List(0,0)

> --- rhs ---

> List(0)

Notice in particular the `i: List(0, 0)`

, which captures the
smallest possible value that doesn't satisfy the invalid property.

Let's try that again, but let's see what else it tried.

`def p1: Property =`

for {

l <- Gen.int(Range.linearFrom(0, -100, 100)).list(Range.linear(0, 100)).log("i")

} yield {

println(l)

l ==== l.distinct

}

`List()`

List(1, -2)

List(-2, -3)

List(1, 4, 1)

List(0, 4, 1)

List(1, 0, 1)

List(1, 0, 0)

List()

List(0, 0)

List()

List(0)

You can see after a few tries Hedgehog finds an invalid example `List(1, 4, 1)`

,
and starts to shrink both the values down to `0`

and also the list size.

### Deterministic results

By default, Hedgehog uses a random seed that is based on the current system time. Normally, this is exactly what you want. However, if you have a failing test, the randomness of the generated test data can make it very difficult to reproduce and analyse the problem — especially if the test is only failing sporadically. In this situation, it would be better if you could get exactly the same generated test data that caused the test to fail.

This is why Hedgehog logs the seed together with the test results. In your console, you should see something like this:

`Using random seed: 58973622580784`

+ hedgehog.PropertyTest$.example1: OK, passed 1 tests

+ hedgehog.PropertyTest$.applicative: OK, passed 1 tests

+ hedgehog.PropertyTest$.applicative shrink: OK, passed 100 tests

Now imagine of these tests fails sporadically in your build pipeline. To analyse the problem locally, you can reproduce this test run by setting the seed to the same value. All you need to do is set the environment variable `HEDGEHOG_SEED`

to the value in question.

Example:

`export HEDGEHOG_SEED=58973622580784`

Now you can reproduce the test run you're interested in. Hedgehog will inform you that it used the seed from the environment variable:

`Using seed from environment variable HEDGEHOG_SEED: 58973622580784`

+ hedgehog.PropertyTest$.example1: OK, passed 1 tests

+ hedgehog.PropertyTest$.applicative: OK, passed 1 tests

+ hedgehog.PropertyTest$.applicative shrink: OK, passed 100 tests

### Classifications

Using `classify`

you can add classifications to your generator's data, for example:

` def testReverse: Property =`

for {

xs <- Gen.alpha.list(Range.linear(0, 10)).forAll

.classify("empty", _.isEmpty)

.classify("nonempty", _.nonEmpty)

} yield xs.reverse.reverse ==== xs

Running that property will produce a result like:

`[info] + hedgehog.examples.ReverseTest.reverse: OK, passed 100 tests`

[info] > 69% nonempty List(a)

[info] > 31% empty List()

Notice how, in addition to the percentage, it also presents a shrunk example for that classifier.

Using `cover`

you may also specify a minimum coverage percentage for the given classification:

` def testReverse: Property =`

for {

xs <- Gen.alpha.list(Range.linear(0, 10)).forAll

.cover(50, "empty", _.isEmpty)

.cover(50, "nonempty", _.nonEmpty)

} yield xs.reverse.reverse ==== xs

`[info] - hedgehog.examples.ReverseTest.reverse: Falsified after 100 passed tests`

[info] > Insufficient coverage.

[info] > 93% nonempty 50% ✓ List(a)

[info] > 7% empty 50% ✗ List()

Finally:

`label(name)`

is an alias for`classify(name, _ => true)`

, and`collect`

is an alias for`labal`

using the value's`toString`

as the classification (label name)

## State

For a separate tutorial on state-based property testing please continue here.