Search code examples
scalascalatest

Scalatest provide position different file for assertion


How do I provide a Position to org.scalatest.Assertions.assert so that it reports a different position rather that the default one?

My problem is that I have a parent test class and a child test class. The child test class uses an assertion method in parent. This causes the test fail messages to be pointed to the parent method instead of the child method.

For example, I have a parent test class as:

abstract class ParentSpec[A <: TestSubject] extends FunSuite {
  protected def checkId(underTest: => A, expectedId: Int): Assertion = {
    assert(underTest.id == expectedId)
  }
}

And a child test class as:

class FooSpec extends ParentSpec[Foo] {
  test("check id") {
    checkId(Foo(1, 2, 3), 999) // this fails
  }
}

If the test fails, then the assertion reports that the issue is from ParentSpec.scala. How can I change this to FooSpec.scala?

I noticed that assert takes in an implicit Position

def assert(condition: Boolean)(implicit prettifier: Prettifier, pos: source.Position): Assertion

can I somehow provide the position?


Solution

  • Just make the checkId method also accept an implicit position:

    protected def checkId(underTest: => A, expectedId: Int)(implicit pos: Position): Assertion
    

    That's it. Now the messages should point at invocations of checkId (unless it's called from yet another function with its own position parameter).

    Now a few words about how this works:

    It's all based on where the compiler looks for implicits. The implicit Position is needed for your assert call which is inside checkId. If checkId accepts an implicit Position parameter by itself, that parameter is simply passed forward to assert. But when this parameter is absent, the compiler will fall back to Position.here (defined in Position companion object). This thing is a macro which materializes the position at which it is called.