Search code examples
scalaintegration-testingfuturespecs2

Specs2 Data Tables with future results


I'm struggling with following problem: I have a method that returns a Future[Result] where Result is the thing I would like to check with a data table row in specs2.

As I understand the following code would block each time and wait for the result to be available.

def getDataForInput(input: String) : Future[Result]

def myTest =
 "input"   |  "expectedResult" |>
  "a"    !  123                |
  "b"      !  0                | {
    (input, result) => getDataForInput input must( beEqualTo(result).await )
  }

Normally I'd like to make all calls asynchronously and then use Future.sequence to transform Seq[Future[Result]] to Future[Seq[Result]] and then run the tests.

Is there a sane way to do this ?


Solution

  • There is no easy way to do that, you need to deconstruct and reconstruct a few things

    type RowResult = (Seq[String], Result)
    
    val table =
      "input"  |  "expectedResult" |
       "123"   !  123              |
       "0"     !  1                |
       "0"     !  0
    
    // get a Future containing all rows and results
    val results: Future[List[RowResult]] = table.rows.toList.traverseU { row =>
      getDataForInput(row.t1).map { i => 
        (row.showCells, (i must beEqualTo(row.t2)).toResult) 
      }
    }
    
    // check the results
    results must beSuccessfulTable(table.titles).await
    

    This uses a custom matcher which will display the table more less nicely

    // the TextTable does an even display of columns
    import org.specs2.text.TextTable
    
    def beSuccessfulTable(titles: Seq[String]): Matcher[List[RowResult]] = { values: List[RowResult] =>
      val overallSuccess = values.map(_._2).reduce(_ and _).isSuccess
      val headers = if (overallSuccess) titles else Seq("")++titles++Seq("")
      val table = TextTable(headers, values.map(resultLine.tupled))
      (overallSuccess, table.show)
    }
    
    // helper method
    def resultLine = (line: Seq[String], result: Result) => {
      val message = if (result.isSuccess) "" else result.message
      result.status +: line :+ message
    }