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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 06:48:42  来源:igfitidea点击:

How to test methods that return Future?

scalaspecs2

提问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 没有提到“最终”

See https://github.com/etorreborre/specs2/blob/master/tests/src/test/scala/org/specs2/matcher/EventuallyMatchersSpec.scala#L10-L43

https://github.com/etorreborre/specs2/blob/master/tests/src/test/scala/org/specs2/matcher/EventuallyMatchersSpec.scala#L10-L43

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 是一种反模式。永远不应该使用它。您可以使用诸如特质ScalaFuturesIntegrationPatienceEventually

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
            }

        }
    }
}