scala 如何测试返回 Future 的方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27750244/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to test methods that return Future?
提问by jaksky
I'd like to test a method that returns a Future. My attempts were as follows:
我想测试一个返回Future. 我的尝试如下:
import org.specs2.mutable.Specification
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
class AsyncWebClientSpec extends Specification{
"WebClient when downloading images" should {
"for a valid link return non-zero content " in {
val testImage = AsyncWebClient.get("https://www.google.cz/images/srpr/logo11ww.png")
testImage.onComplete { res =>
res match {
case Success(image) => image must not have length(0)
case _ =>
}
AsyncWebClient.shutDown
}
}
}
}
Apart from the fact that I am unable to make this code work I guess that there could be a better way of testing a futures with a Future-oriented matcher.
除了我无法使此代码工作这一事实之外,我想可能有更好的方法来使用Future面向匹配器测试期货。
How to do it properly in specs2?
如何在specs2中正确地做到这一点?
回答by Eric
You can use the Matcher.awaitmethod to transform a Matcher[T]into a Matcher[Future[T]]:
您可以使用该Matcher.await方法将 aMatcher[T]转换为 a Matcher[Future[T]]:
val testImage: Future[String] =
AsyncWebClient.get("https://www.google.cz/images/srpr/logo11ww.png")
// you must specify size[String] here to help type inference
testImage must not have size[String](0).await
// you can also specify a number of retries and duration between retries
testImage must not have size[String](0).await(retries = 2, timeout = 2.seconds)
// you might also want to check exceptions in case of a failure
testImage must throwAn[Exception].await
回答by subandroid
Took me awhile to find this so thought I'd share. I should've read the release notes. In specs2 v3.5, it is required to use implicit ExecutionEnv to use await for future. This can also be used for future transformation (i.e. map) see http://notes.implicit.ly/post/116619383574/specs2-3-5.
我花了一段时间才找到这个,所以我想分享一下。我应该阅读发行说明。在 specs2 v3.5 中,需要使用隐式 ExecutionEnv 才能使用 await for future。这也可以用于未来的转换(即地图),参见http://notes.implicit.ly/post/116619383574/specs2-3-5。
excerpt from there for quick reference:
从那里摘录以供快速参考:
import org.specs2.concurrent.ExecutionEnv
class MySpec extends mutable.Specification {
"test of a Scala Future" >> { implicit ee: ExecutionEnv =>
Future(1) must be_>(0).await
}
}
回答by Mikhail Golubtsov
There is a nice thing for that in specs2 - implicit awaitmethod for Future[Result]. If you take advantage of future transformations you can write like this:
在 specs2 中有一件好事 - 的隐式await方法Future[Result]。如果你利用未来的转换,你可以这样写:
"save notification" in {
notificationDao.saveNotification(notification) map { writeResult =>
writeResult.ok must be equalTo (true)
} await
}
Future composition comes to the rescue when some data arrangement with async functions is needed:
当需要一些具有异步功能的数据安排时,未来的组合就会派上用场:
"get user notifications" in {
{
for {
_ <- notificationDao.saveNotifications(user1Notifications)
_ <- notificationDao.saveNotifications(user2Notifications)
foundUser1Notifications <- notificationDao.getNotifications(user1)
} yield {
foundUser1Notifications must be equalTo (user1Notifications)
}
} await
}
Note how we have to use an additional block around for-comprehension to convince compiler. I think it's noisy, so if we turn awaitmethod in a function we come up with a nicer syntax:
请注意我们如何在 for-comprehension 周围使用额外的块来说服编译器。我认为它很吵,所以如果我们await在函数中转换方法,我们会得到更好的语法:
def awaiting[T]: Future[MatchResult[T]] => Result = { _.await }
"get user notifications" in awaiting {
for {
_ <- notificationDao.saveNotifications(user1Notifications)
_ <- notificationDao.saveNotifications(user2Notifications)
foundUser1Notifications <- notificationDao.getNotifications(user1)
} yield {
foundUser1Notifications must be equalTo (user1Notifications)
}
}
回答by SemanticBeeng
Wondering why @etorreborre did not mention "eventually"
想知道为什么@etorreborre 没有提到“最终”
class EventuallyMatchersSpec extends Specification with FutureMatchers with ExpectationsDescription { section("travis")
addParagraph { """
`eventually` can be used to retry any matcher until a maximum number of times is reached
or until it succeeds.
""" }
"A matcher can match right away with eventually" in {
1 must eventually(be_==(1))
}
"A matcher can match right away with eventually, even if negated" in {
"1" must not (beNull.eventually)
}
"A matcher will be retried automatically until it matches" in {
val iterator = List(1, 2, 3).iterator
iterator.next must be_==(3).eventually
}
"A matcher can work with eventually and be_== but a type annotation is necessary or a be_=== matcher" in {
val option: Option[Int] = Some(3)
option must be_==(Some(3)).eventually
}
回答by Michael Zajac
onCompletereturns Unit, so that block of code returns immediately and the test ends before being able to do anything. In order to properly test the result of a Future, you need to block until it completes. You can do so using Await, and setting a maximum Durationto wait.
onCompleteReturns Unit,以便代码块立即返回并且测试在能够做任何事情之前结束。为了正确测试 a 的结果Future,您需要阻塞直到它完成。您可以使用Await, 并设置Duration等待的最大值。
import scala.concurrent._
import scala.concurrent.duration._
Await.result(testImage, Duration("10 seconds")) must not have length(0)
回答by epinal
Await is an anti pattern. Shouldn't ever use it.
You can use traits like ScalaFutures, IntegrationPatience, and Eventually.
Await 是一种反模式。永远不应该使用它。您可以使用诸如特质ScalaFutures,IntegrationPatience和Eventually。
Example:
例子:
import org.specs2.mutable.Specification
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
import scala.concurrent.Future
class AsyncWebClientSpec extends Specification with ScalaFutures
with IntegrationPatience{
"WebClient when downloading images" should {
"for a valid link return non-zero content " in {
whenReady(Future.successful("Done")){ testImage =>
testImage must be equalTo "Done"
// Do whatever you need
}
}
}
}

