Table Driven Approach For Testing

Table of contents
Reading Time: 3 minutes

Most of us would have been in a situation where multiple test cases are same but the input data isn’t, Or even when input data is same and is used for multiple test cases.

So In cases like these we can use Table driven approach, it is a way of testing that allows you to write minimal test code with increased readability.

Let’s understand this with an example, imagine you want to test this Shape Class

class Shape {

  def calculateArea(shape: String, sideOne: Int, sideTwo: Int): Int = {

    def calculateShapeArea(sideOne: Int, sideTwo: Int, area: (Int, Int) => Int) = area(sideOne, sideTwo)

    shape.toLowerCase match {
      case "rectangle" | "parallelogram" => calculateShapeArea(sideOne, sideTwo, (a, b) => a * b)
      case "rhombus" => calculateShapeArea(sideOne, sideTwo, (a, b) => a * b / 2)
      case _ => -1
    }
  }

}

Here we can use table-driven approach, it will make our code minimal and also save time.

Let’s understand this approach in scala

Trait TableDrivenPropertyChecks of Scalatest can be used or its companion object. It has two methods –

forAll: checks properties against the rows of table

wherever: It is used to indicate a property need only hold whenever some condition is true

It allows you to create tables with between 1 and 22 columns and any number of rows. You create a table by passing tuples to one of the factory methods of object Table. Each tuple must have the same arity (number of members). The first tuple you pass must all be strings because it defines names for the columns. Subsequent tuples define the data. After the initial tuple that contains string column names, all tuples must have the same type. For example, if the first tuple after the column names contains two Ints, all subsequent tuples must contain two Int (i.e., have type Tuple2[Int, Int]).

Testing of Shape Class using TableDrivenPropertyChecks

import org.scalatest.prop.TableDrivenPropertyChecks._
import org.scalatest.{Matchers, WordSpec}

class ShapeSpec extends WordSpec with Matchers {

  val shape: Shape = new Shape()
  val inputData = Table(
    ("testCase", "shapeType", "result"),
    ("shape is rectangle", "rectangle", 36),
    ("shape is rhombus", "rhombus", 18),
    ("shape is triangle", "triangle", -1)
  )

  "calculate Area" should {
    forAll(inputData) { (testCase, shapeType, area) =>
      testCase in {
        val result = shape.calculateArea(shapeType, 6, 6)
        result shouldBe area
      }
    }
  }

}

Each forAll method takes two parameter lists, as shown in the example. The first parameter list is a table. The second parameter list is a function whose argument types and number match that of the tuples in the table. So here as our tuples in the table contain String, String, Int, then the function supplied to forAll must take 3 parameters, a String, a String, and an Int. The forAll method will pass each row of data to the function, and generate a TableDrivenPropertyCheckFailedException if the function completes abruptly for any row of data with an exception that would normally cause a test to fail in ScalaTest.

A DiscardedEvaluationException is thrown by the whenever method (also defined in this trait) to indicate a condition required by the property function not met by a row of passed data, this will simply cause forAll to skip that row of data.

Hopefully, you liked this approach of testing, and it helped you to write better code for testing.


knoldus-advt-sticker