0

I am trying to use ScalaCheck's forAll in conjunction with behavior functions but I'm running into issues. The behavior functions look like this:

def someBehaviour(args: FunArgs)

where FunArgs is basically a union type of the various possibilities that I want to verify. So, there is a FunArgs1 and a FunArgs2 that both inherit from FunArgs but have different members (e.g. one has two Ints whereas the other has a String, a Long and an Int). The actual function I want to test deals with these different cases internally.

sealed trait FunArgs
final case class FunArgs1(a: Int, b: Int) extends FunArgs
final case class FunArgs2(x: Long, y: Int, z: String) extends FunArgs

sealed trait FunRes
final case class FunRes1(...) extends FunRes
final case class FunRes2(...) extends FunRes

def someFun(args: FunArgs): FunRes = args match {
  // do stuff and return some subclass of FunRes
}

trait SomeBehaviors { this: UnitSpec =>

  def someBehavior(args: FunArgs) {

    it should "do stuff " in {
      val result = someFun(args) // creates an instance of FunRes
      ...
      result should contain theSameElementsAs expected
    }

    it should "do more stuff" in {
      // similar to before
      ...
      result should contain theSameElementsAs expected
    }
  }
}

Inside the behavior function are multiple in clauses because all functions need to satisfy the same behavior.

The problem is that I cannot do this because it will create duplicate test names:

forAll { (a: Int, b: Int) =>
  someBehavior(FunArgs1(a, b))
}

But I also cannot do this because of 'should' inside 'in':

"someFun1" should "do stuff" in {
  forAll { (a: Int, b: Int) =>
    someBehaviour(FunArgs1(a, b))
  }
}

And the line below doesn't give me the possibility of using generators because I would have to put the forAll inside the behavior where I do not necessarily know how many and what types the arguments are.

"someFun1" should behave like someBehaviour(MakeFunArgs1(a, b))

Is there a way to do what I want to?

Max Power
  • 952
  • 9
  • 24
  • If I correctly understand what you're trying to achieve, you should probably define a custom `Gen[FunArgs]` generator (named, say, `funArgs`). Then you could easily write a property of the form `forAll(funArgs) { fa => someBehavior(fa) }`, which would cover all `FunArgs` subtypes, as opposed to writing one property for each subtype. – jub0bs Jul 04 '17 at 20:24

1 Answers1

0

You can do something like this

trait SomeBehaviors { this: UnitSpec =>

  def someBehavior(index:Int, args: FunArgs) {

    it should s"do stuff $index" in {
      val result = someFun(args) // creates an instance of FunRes
      ...
      result should contain theSameElementsAs expected
    }

    it should "do more stuff $index" in {
      // similar to before
      ...
      result should contain theSameElementsAs expected
    }
  }
}

You can call following with unique testCaseName:

var index=0
forAll { (a: Int, b: Int) =>
  someBehavior(index,FunArgs1(a, b))
  index = index+1
}

instead of index you can also generate a unique name using a & b, that'll solve the problem.

Atiq
  • 396
  • 1
  • 3
  • 10
  • That'll create a potentially huge number of sub-cases that will be listed even though they should have only a single entry in the test report. They all check the same thing after all, just for different inputs. – Max Power Jun 22 '17 at 11:07