Search code examples
scalatest

How to have scalatest/sbt report labelled cases from TableDrivenPropertyChecks


I am new to scalatest and a bit lost in the gigantic inconsistent mess of features

I am trying to write parameterized tests. It seems that the way to do that is via TableDrivenPropertyChecks

I managed to get it working with a test like:

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.prop.TableDrivenPropertyChecks

class MyTest extends AnyFunSpec with TableDrivenPropertyChecks {

  describe("the test") {

    val cases = Table(
      ("label", "a", "b"),
      ("case 2 > 1", 1, 2),
      ("case 4 > 3", 3, 4),
    )

    it("should...") {
      forAll(cases) { (name: String, a: Int, b: Int) =>
        assert(b > a)
      }
    }
  }
}

It's fine, but when I run it under sbt I just see:

[info] MyTest:
[info] the test
[info] - should...

If I inject a bad case into the table I can see the test fail so I know they are all being tested.

What I want is for the test runner to report something for each case in the table.

I have seen examples which look like they would provide this, e.g.: https://blog.knoldus.com/table-driven-testing-in-scala/

import org.scalatest.FreeSpec
import org.scalatest._
import org.scalatest.prop.TableDrivenPropertyChecks

class OrderValidationTableDrivenSpec extends FreeSpec with TableDrivenPropertyChecks with Matchers {

  "Order Validation" - {
    "should validate and return false if" - {
      val orders = Table(
        ("statement"                          , "order")
        , ("price is negative"                , Order(quantity = 10, price = -2))
        , ("quantity is negative"             , Order(quantity = -10, price = 2))
        , ("price and quantity are negative"  , Order(quantity = -10, price = -2))
      )

      forAll(orders) {(statement, invalidOrder) =>
        s"$statement" in {
          OrderValidation.validateOrder(invalidOrder) shouldBe false
        }
      }
    }
  }
}

But if I try to do this in my test:

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.prop.TableDrivenPropertyChecks

class MyTest extends AnyFunSpec with TableDrivenPropertyChecks {

  describe("the test") {

    val cases = Table(
      ("label", "a", "b"),
      ("case 2 > 1", 1, 2),
      ("case 3 > 4", 3, 4),
    )

    it("should...") {
      forAll(cases) { (label: String, a: Int, b: Int) =>
        s"$label" in {
          assert(b > a)
        }
      }
    }
  }
}

...I get the error: value in is not a member of String from the s"$label" in { part.

How do I get my TableDrivenPropertyChecks to report something for each case in the table?


Solution

  • One of cool features of ScalaTest is that test is not a method, so you can define tests inside a loop:

    Table(
      ("a", "b"),
      (1, 2),
      (3, 4)
    ).forEvery({ (a: Int, b: Int) => {
      it(s"should pass if $b is greater then $a") {
        assert(b > a)
      }
    }})
    

    Or even without using Table, just with any collection:

    Seq(
      (1, 2),
      (3, 4)
    ).foreach {
      case (a: Int, b: Int) =>
        it(s"should pass if $b is greater then $a") {
          assert(b > a)
        }
    }
    

    This way you will get one test per each iteration of a loop, so all the cases will be executed independently, while in case of a loop in a single test it will fail on first failed assertion and won't check remaining cases.