タイトルが全てなのですが、具体的にどうやるかも書いておきます。
ドキュメントとしては、 http://www.scalatest.org/user_guide/using_the_runner#scalingTimeSpans の、 Specifying a span scale factor ってあたりに書いてあります。引用しておきます。
If you specify a integer or floating point span scale factor with -F, trait ScaledTimeSpans trait will return the specified value from its implementation of spanScaleFactor. This allows you to tune the "patience" of a run (how long to wait for asynchronous operations) from the command line. For more information, see the documentation for trait ScaledTimeSpans.
なお、以下に引用しているソースコードは、3.1.xですが、実行自体は、ScalaTest 3.0.5で試してます。
Example Code
こんな感じで書いてみました。
package example
import org.scalatest._
import org.scalatest.concurrent._
import scala.concurrent._
class HelloSpec extends FlatSpec with Matchers with ScalaFutures {
import ExecutionContext.Implicits.global
"The Hello object" should "say hello" in {
Future {
Thread.sleep(1000L);
println("hoge");
}.futureValue
Hello.greeting shouldEqual "hello"
}
}
run test
sbtでtestを実行すると、以下のようなエラーになります。
[info] - should say hello *** FAILED ***
[info] A timeout occurred waiting for a future to complete. Queried 10 times, sleeping 15 milliseconds between each query. (HelloSpec.scala:14)
分析
これは、
}.futureValue
のところでエラーになっています。
といいますのも、Futureの中で1秒スリープしているのですが、これが待ち切れずにtimeoutしています。
このFutureをどれだけ待つかってあたりについては、
class HelloSpec extends FlatSpec with Matchers with ScalaFutures {
でMixinしている ScalaFutures をたどっていくと見えてくるのですが、最終的には、AbstractPatienceConfigurationで宣言されているcase classの、 PatienceConfig が適用されています。
ここで設定されている、
timeout: Span = scaled(Span(150, Millis)), interval: Span = scaled(Span(15, Millis))
っていうのが適用されているため、Futureの中で実施している Thread.sleep
を待ち切れずにエラーとなっています。
ここで設定されているtimeoutするまでintervalあけてリトライしていく処理になっており、15ミリ秒間をあけつつ、10回リトライして全部で150ミリ秒待ったけど、終わらなかったからエラーってロジックになっています。1
係数をかける
ScalaTestのドキュメントに、
argument | description | example |
---|---|---|
-F <span scale factor> | a factor by which to scale time spans (Note: only one -F is allowed) |
-F 10 or -F 2.5 |
とあり、こちらを実行時に指定すると、係数をかけることができます。
sbtで実行する際は、以下のどちらかの方法で実行できます。ここでは、10を係数としてかけてます。
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-F", "10")
もしくは、testOnlyで実行するときの引数に渡せます。
sbt:Hello> testOnly example.HelloSpec -- -F 10
どうも、build.sbtで設定しながら、testOnlyの実行時に渡すということはできない様子です。
まとめ
Thread.sleepするような箇所が環境依存で、ローカルのハイスペックMacBook Proでは通るテストが、Travisで実行するとTimeoutで落ちちゃうってときに、使うと良さげです。
build.sbtにベタって、10って書いちゃうと、ローカルとTravisでって分けられないので、10ってあたりを環境変数とかにして、.travis.ymlとかに環境変数を指定するみたいな運用になると良いのかなって思ってます。
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-F", sys.env.getOrElse("SBT_TEST_TIME_FACTOR", "1"))
以上です。