Expectations (assertions)

Expectations are pure, composable values. This forces developers to separate the test's checks from the scenario, which is generally cleaner/clearer.

The easiest way to construct expectactions is to call the expect macro. The clue function can be used to investigate failures.

TL;DR

Example suite

import weaver._
import cats.effect.IO

object ExpectationsSuite extends SimpleIOSuite {

  object A {
    object B {
      object C {
        def test(a: Int) = a + 5
      }
    }
  }

  pureTest("Simple expectations (success)") {
    val z = 15
    
    expect(A.B.C.test(z) == z + 5)
  }
  
  pureTest("Simple expectations (failure)") {
    val z = 15
    
    expect(clue(A.B.C.test(z)) % 7 == 0)
  }


  pureTest("And/Or composition (success)") {
    expect(1 != 2) and expect(2 != 1) or expect(2 != 3)
  }

  pureTest("And/Or composition (failure)") {
    (expect(1 != clue(2)) and expect(2 == clue(1))) or expect(2 == clue(3))
  }

  pureTest("Varargs composition (success)") {
    // expect(1 + 1 == 2) && expect (2 + 2 == 4) && expect(4 * 2 == 8)
    expect.all(1 + 1 == 2, 2 + 2 == 4, 4 * 2 == 8)
  }

  pureTest("Varargs composition (failure)") {
    // expect(1 + 1 == 2) && expect (2 + 2 == 4) && expect(4 * 2 == 8)
    expect.all(clue(1 + 1) == 2, clue(2 + 2) == 5, clue(4 * 2) == 8)
  }

  pureTest("Working with collections (success)") {
    forEach(List(1, 2, 3))(i => expect(i < 5)) and
      forEach(Option("hello"))(msg => expect.same(msg, "hello")) and
      exists(List("a", "b", "c"))(i => expect(i == "c")) and
      exists(Vector(true, true, false))(i => expect(i == false))
  }

  pureTest("Working with collections (failure 1)") {
    forEach(Vector("hello", "world"))(msg => expect.same(msg, "hello"))
  }

  pureTest("Working with collections (failure 2)") {
    exists(Option(39))(i => expect(clue(i) > 50))
  }

  import cats.Eq
  case class Test(d: Double)

  implicit val eqTest: Eq[Test] = Eq.by[Test, Double](_.d)

  pureTest("Strict equality (success)") {
    expect.eql("hello", "hello") and
      expect.eql(List(1, 2, 3), List(1, 2, 3)) and
      expect.eql(Test(25.0), Test(25.0))  
  }

  pureTest("Strict equality (failure 1)") {
    expect.eql("hello", "world")
  }

  pureTest("Strict equality (failure 2)") {
    expect.eql(List(1, 2, 3), List(1, 19, 3))
  }

  pureTest("Strict equality (failure 3)") {
    expect.eql(Test(25.0), Test(50.0))
  }

  // Note that we don't have an instance of Eq[Hello]
  // anywhere in scope
  class Hello(val d: Double) {
    override def toString = s"Hello to $d"

    override def equals(other: Any) = 
      if(other != null && other.isInstanceOf[Hello])
        other.asInstanceOf[Hello].d == this.d
      else 
        false
  }

  pureTest("Relaxed equality comparison (success)") {
    expect.same(new Hello(25.0), new Hello(25.0))
  }
  
  pureTest("Relaxed equality comparison (failure)") {
    expect.same(new Hello(25.0), new Hello(50.0))
  }

  pureTest("Non macro-based expectations") {
    val condition : Boolean = false
    if (condition) success else failure("Condition failed")
  }

  test("Failing fast expectations") {
    for {
      h <- IO.pure("hello")
      _ <- expect(clue(h).isEmpty).failFast
    } yield success
  }
}

Example suite report

repl.MdocSessionMdocAppExpectationsSuite
Simple expectations (success) 12ms
And/Or composition (success) 25ms
Varargs composition (success) 1ms
Working with collections (success) 15ms
Strict equality (success) 12ms
Relaxed equality comparison (success) 3ms
Simple expectations (failure) 25ms
And/Or composition (failure) 24ms
Varargs composition (failure) 8ms
Working with collections (failure 1) 16ms
Working with collections (failure 2) 4ms
Strict equality (failure 1) 10ms
Strict equality (failure 2) 2ms
Strict equality (failure 3) 1ms
Relaxed equality comparison (failure) 1ms
Non macro-based expectations 0ms
Failing fast expectations 7ms

*************FAILURES*************
repl.MdocSessionMdocAppExpectationsSuite
Simple expectations (failure) 25ms
  assertion failed (expectations.md:33)

  Clues {
    A.B.C.test(z): Int = 20
  }
And/Or composition (failure) 24ms
 [0] assertion failed (expectations.md:42)
 [0] 
 [0] Clues {
 [0]   1: Int = 1
 [0] }


 [1] assertion failed (expectations.md:42)
 [1] 
 [1] Clues {
 [1]   3: Int = 3
 [1] }
Varargs composition (failure) 8ms
  assertion failed (expectations.md:52)

  Clues {
    1 + 1: Int = 2
    2 + 2: Int = 4
    4 * 2: Int = 8
  }
Working with collections (failure 1) 16ms
  Values not equal: (expectations.md:63)

  
=> Diff (- obtained+ expected)
  -hello
  +world Working with collections (failure 2) 4ms
  assertion failed (expectations.md:67)

  Clues {
    i: Int = 39
  }
Strict equality (failure 1) 10ms
  Values not equal: (expectations.md:82)

  
=> Diff (- obtained+ expected)
  -world
  +hello Strict equality (failure 2) 2ms
  Values not equal: (expectations.md:86)

  
=> Diff (- obtained+ expected)
  -List(1, 19, 3)
  +List(1, 2, 3) Strict equality (failure 3) 1ms
  Values not equal: (expectations.md:90)

  
=> Diff (- obtained+ expected)
  -Test(50.0)
  +Test(25.0) Relaxed equality comparison (failure) 1ms
  Values not equal: (expectations.md:110)

  
=> Diff (- obtained+ expected)
  -Hello to 50.0
  +Hello to 25.0 Non macro-based expectations 0ms
  Condition failed (expectations.md:115) Failing fast expectations 7ms
  assertion failed (expectations.md:121)

  Clues {
    h: String = hello
  }
Total 17, Failed 11, Passed 6, Ignored 0, Cancelled 0