danio
danio

Reputation: 8653

Can Specs2 Result.foreach be made to work like a matcher?

Using Specs2, if I do:

    1 must beEqualTo(2)
    1 must beEqualTo(1)

The test fails (as expected)

If I do:

    Result.foreach(1 to 10) { i =>
      i must_== 2
    }
    Result.foreach(1 to 10) { i =>
      i must_== i
    }

The test passes

For Result.foreach I have to use and to make the test fail (as expected):

    Result.foreach(1 to 10) { i =>
      i must_== 2
    } and
    Result.foreach(1 to 10) { i =>
      i must_== i
    }

Why is this? Is there a way to make it work in a less surprising way? This is very error prone - it's way too easy to not notice somebody didn't type and

I checked the Specs2 user guide but there's no mention of this behaviour that I can find.

Upvotes: 1

Views: 269

Answers (2)

Eric
Eric

Reputation: 15557

This should probably be better documented. The Result.foreach function is indeed supposed to be non side-effecting. If you want side-effects you can call foreach directly in your specification because this one is wired in with the "throwing" behaviour of a mutable specification:

class TestMutableSpec extends mutable.Specification {

  "foreach must fail" >> {
    foreach(1 to 10) { i =>
      i must_== 2
    }
    foreach(1 to 10) { i =>
      i must_== i
    }
  }
}

returns

[info] TestMutableSpec
[error] x foreach must fail
[error]  There are 9 failures
[error]  1 != 2
[error]  3 != 2
[error]  4 != 2
[error]  5 != 2
[error]  6 != 2
[error]  7 != 2
[error]  8 != 2
[error]  9 != 2
[error]  10 != 2 (TestSpec.scala:19)
[info] Total for specification TestMutableSpec
[info] Finished in 121 ms
[info] 1 example, 1 failure, 0 error

Upvotes: 2

Michał Pawlicki
Michał Pawlicki

Reputation: 421

Workaround: mix AllExpectations into your Specification:

class QuickStartSpec extends mutable.Specification with AllExpectations {
  "foreach must fail" >> {
    Result.foreach(1 to 10) { i =>
      i must_== 2
    }
    Result.foreach(1 to 10) { i =>
      i must_== i
    }
  }
}

This produces:

sbt> testOnly QuickStartSpec
[info] QuickStartSpec
[error] x foreach must fail
[error]  1 != 2 (QuickStartSpec.scala:8)

Note: I'm aware of the fact that this is not the primary purpose of the AllExpectations trait. However, this seems a sensible solution, given that Result.foreach behaves in a different way than a matcher inside a mutable specification.

Also, according to the docs, the goal of "unit specifications" is to let users have multiple expectations per example without the need to use and. In light of this, the behavior of Result.foreach indeed seems misleading. If you think this is a bug, you may consider opening an issue.

Upvotes: 1

Related Questions