LoginSignup
8
1

More than 5 years have passed since last update.

scalatestでFutureをテストする方法3通り

Posted at

問題

Futureを使用したテストがかけない。
単純にFutureの値のmapで以下のように書こうとしても、map内のassertの実行前にテストが終了してしまう

    HelloFuture.hello().map { s =>
      assert(s == "hello!")
    }

例としてこちらの単純なクラスのhello()をテストする。

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

object HelloFuture {

  def hello(): Future[String] = {
    Future {
      "hello!"
    }
  }
}

Await.resultを使用

import scala.concurrent.Await
import scala.concurrent.duration._

class HelloFutureWithAwaitSpec extends FlatSpec {
  "hello method" should "return string 'hello!'" in {
    val result = Await.result(HelloFuture.hello(), 500 millis)
    assert(result == "hello!")
  }
}

参考:Futures and Promises | Scala Documentation

ScalaFuturesを使用

scalatestに付属している、テスト専用のメソッドであるfutureValueを使用して値を取り出す。
Await.resultよりコードの見通しがよい。

import org.scalatest.FlatSpec
import org.scalatest.concurrent.ScalaFutures

class HelloFutureWithFutureValueSpec extends FlatSpec with ScalaFutures {
  "hello method with futureValue" should "return string 'hello!'" in {
    assert(HelloFuture.hello().futureValue == "hello!")
  }
}

ちなみに、org.scalatest.concurrent.ScalaFuturesのwhenReadyを使用してもテストがかける。

import org.scalatest.FlatSpec
import org.scalatest.concurrent.ScalaFutures

class HelloFutureWithWhenReadySpec extends FlatSpec with ScalaFutures {
  "hello method with whenReady" should "return string 'hello!'" in {
    whenReady(HelloFuture.hello()) { result =>
      assert(result == "hello!")
    }
  }
}

いずれも、timeout時間と再実行までの間隔intervalを設定できるようなので、Await.resultと同じようにある程度待たないとFutureの処理が終わらない場合も使える。

参考: ScalaTest 3.0.1 - org.scalatest.concurrent.ScalaFutures

Async系のSuiteを使用

scalatest3.xから登場。Future型のmapのブロック内でassertを実行しても、正常に実行可能。

例ではAsyncFlatSpecだが、FlatSpec以外のscalatestが提供している各種suiteのAsync版がある。

import org.scalatest.AsyncFlatSpec

class HelloFutureWithAsyncSpec extends AsyncFlatSpec {
  "hello method with async assert" should "return string 'hello!'" in {
    HelloFuture.hello() map { result =>
      assert(result == "hello!")
    }
  }
}

ThreadSleepを仕込んで10秒またせたが、終了まできちんと待ってからassertを実行してくれていた。
Future特有のメソッドをわざわざ使わないので実装コードと同じ感覚でかけるのがよい。

参考:ScalaTest

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1