erip
erip

Reputation: 16995

How can I define `LoneElement` and `Emptiness` for composed collections?

I have a case class which is a simple wrapper around a collection:

final case class Foo(foos: Seq[Int])

and in my tests, I'd like to make assertions about the emptiness and lone elements of Foo#foos. I'm currently circumventing the problem by accessing Foo#foos directly:

foo.foos shouldBe empty

or

foo.foos.loneElement should===(1)

This works, but breaks encapsulation.

I've looked through the scalatest docs, but haven't found a way to define these operators outside of a companion.

How can I define them? Bonus points for inlining them in the tests.

Upvotes: 0

Views: 179

Answers (1)

Andrey Tyukin
Andrey Tyukin

Reputation: 44992

Define some implicits to deal with Foo (in the test-directory, not in the main source code tree):

import org.scalatest.enablers.Emptiness

trait FooImplicits {

  case class FooLoneElementWrapper(wrapped: Foo) {
    def loneElement: Int = {
      assert(wrapped.foos.size == 1)
      wrapped.foos(0)
    }
  }
  implicit def fooCanHazLoneElement(foo: Foo) = FooLoneElementWrapper(foo)
  implicit object FooEmptiness extends Emptiness[Foo] {
    def isEmpty(foo: Foo) = foo.foos.isEmpty
  }

}

Now simply mixin the trait FooImplicits into the FlatSpec where you would like to use it:

import org.scalatest._

class FooSpec extends FlatSpec with Matchers with FooImplicits {

  "Foo" should "be empty when it's empty" in {
    Foo(Seq()) shouldBe empty
  }

  it should "have loneElements" in {
    val x = Foo(Seq(1))
    x.loneElement should equal (1)
  }
}

Profit:

[info] Foo
[info] - should be empty when it's empty
[info] - should have loneElements

Notice that the FooImplicits can be defined in the same package as Foo in the main tree, even though it is in a completely different test-source-code tree. This way, it can access package-visible members of Foo.

Upvotes: 1

Related Questions