scala ScalaTest:在失败的期货中断言异常(非阻塞)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20925352/
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
ScalaTest: Assert exceptions in failed futures (non-blocking)
提问by flavian
import org.scalatest.{ FlatSpec, Matchers, ParallelTestExecution }
import org.scalatest.concurrent.ScalaFutures
import org.apache.thrift.TApplicationException
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution {
it should "throw org.apache.thrift.TApplicationException for invalid Ids" in {
val future: Future[Response] = ThriftClient.thriftRequest
whenReady(future) {
res => {
intercept[TApplicationException] {
}
}
}
}
}
Question:How do you assert expected failures in Futures without blocking? The above doesn't work, the exception is thrown before the interceptblock.
问题:如何在不阻塞的情况下断言 Futures 中的预期失败?以上不起作用,在intercept块之前抛出异常。
采纳答案by som-snytt
Note: leaving this answer because the OP found it helpful, but for Scala Futures see the other answer.
注意:留下这个答案是因为 OP 发现它有帮助,但对于 Scala Futures,请参阅另一个答案。
This is a bit boilerplated, but Waiterfrom AsyncAssertions:
这有点样板化,但Waiter来自AsyncAssertions:
import org.scalatest.{ FlatSpec, Matchers, ParallelTestExecution }
import org.scalatest.concurrent.{ ScalaFutures, AsyncAssertions, PatienceConfiguration }
import concurrent.Future
import concurrent.ExecutionContext.Implicits._
import util._
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with AsyncAssertions {
it should "throw for invalid Ids" in {
val f: Future[Int] = new Goof().goof
val w = new Waiter
f onComplete {
case Failure(e) => w(throw e); w.dismiss()
case Success(_) => w.dismiss()
}
intercept[UnsupportedOperationException] {
w.await
}
}
}
given
给予
import concurrent.Future
import concurrent.ExecutionContext.Implicits._
class Goof {
def goof(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
throw new UnsupportedOperationException
}
def goofy(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
throw new NullPointerException
}
def foog(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
7
}
}
In other words,
换句话说,
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with AsyncAssertions {
it should "throw for invalid Ids" in {
val f: Future[Int] = new Goof().goof
import Helper._
f.failing[UnsupportedOperationException]
}
}
object Helper {
implicit class Failing[A](val f: Future[A]) extends Assertions with AsyncAssertions {
def failing[T <: Throwable](implicit m: Manifest[T]) = {
val w = new Waiter
f onComplete {
case Failure(e) => w(throw e); w.dismiss()
case Success(_) => w.dismiss()
}
intercept[T] {
w.await
}
}
}
}
Or, if you have multiple futures and you want the first non-conforming future to fail the test:
或者,如果您有多个期货,并且希望第一个不合格的期货未能通过测试:
trait FailHelper extends Assertions with AsyncAssertions with PatienceConfiguration {
def failingWith[T <: Throwable : Manifest](fs: Future[_]*)(implicit p: PatienceConfig) {
val count = new java.util.concurrent.atomic.AtomicInteger(fs.size)
val w = new Waiter
for (f <- fs) f onComplete {
case Success(i) =>
w(intercept[T](i))
println(s"Bad success $i")
w.dismiss()
case Failure(e: T) =>
println(s"Failed $e OK, count ${count.get}")
w(intercept[T](throw e))
if (count.decrementAndGet == 0) w.dismiss()
case Failure(e) =>
println(s"Failed $e Bad")
w(intercept[T](throw e))
w.dismiss()
}
w.await()(p)
}
}
with usage
使用
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with FailHelper {
it should "throw for invalid Ids" in {
val sut = new Goof()
import sut._
val patienceConfig = null // shadow the implicit
implicit val p = PatienceConfig(timeout = 10 seconds)
// all should fail this way
//failingWith[UnsupportedOperationException](goof(), goofy(3), foog(5))
//failingWith[UnsupportedOperationException](goof(), foog(5))
failingWith[UnsupportedOperationException](goof(), goof(2), goof(3))
}
}
Inspired by this unloved answer.
受到这个不受欢迎的答案的启发。
回答by Steven Bakhtiari
I know this is probably a bit late, but ScalaTest provides this feature out of the box (I believe since version 2) by mixing in the ScalaFutures trait, or using it directly in your test functions. Behold!
我知道这可能有点晚了,但是 ScalaTest 通过混合 ScalaFutures 特性或直接在您的测试函数中使用它来提供开箱即用的功能(我相信从第 2 版开始)。看!
test("some test") {
val f: Future[Something] = someObject.giveMeAFuture
ScalaFutures.whenReady(f.failed) { e =>
e shouldBe a [SomeExceptionType]
}
}
Or you can perform some other assertions in there. Basically, if your future doesn't fail like you expect, the test will fail. If it fails, but throws a different exception, the test will fail. Nice and easy! =]
或者您可以在那里执行一些其他断言。基本上,如果你的未来没有像你期望的那样失败,测试就会失败。如果它失败,但抛出不同的异常,则测试将失败。好,易于!=]
cheeky edit:
厚颜无耻的编辑:
You can also use this method to test anything that returns a future:
您还可以使用此方法来测试返回未来的任何内容:
test("some test") {
val f: Future[Something] = someObject.giveMeAFuture
ScalaFutures.whenReady(f) { s =>
// run assertions against the object returned in the future
}
}
Most recent edit!
最新编辑!
I just wanted to update this answer with more useful information based on newer versions of Scala test. The various spec traits now all have async support, so instead of extending, say, WordSpec, you would instead extend AsyncWordSpec, and instead of relying on the whenReadycalls as above, you would just map over your futures directly in the test.
我只是想根据更新版本的 Scala 测试用更多有用的信息更新这个答案。各种规范特征现在都具有异步支持,因此不是扩展,比如说,,WordSpec而是扩展AsyncWordSpec,而不是依赖于上述whenReady调用,您只需直接在测试中映射您的期货。
Example:
例子:
class SomeSpec extends Async[*]Spec with Matchers {
...
test("some test") {
someObject.funcThatReturnsAFutureOfSomething map { something =>
// run assertions against the 'something' returned in the future
}
}
}
回答by easel
This was buried in a comment as well, but Scalatest's FutureValues mixin has you covered.
这也隐藏在评论中,但 Scalatest 的 FutureValues mixin 已经涵盖了。
Just use f.failed.futureValue shouldBe an[TApplicationException]
只需使用 f.failed.futureValue shouldBe an[TApplicationException]
回答by Brian Low
ScalaTest 3.0 adds async versions of the spec traitslike AsyncFreeSpec:
ScalaTest 3.0 添加了规范特征的异步版本,例如AsyncFreeSpec:
import org.scalatest.{AsyncFlatSpec, Matchers}
import scala.concurrent.Future
class ScratchSpec extends AsyncFlatSpec with Matchers {
def thriftRequest = Future { throw new Exception() }
it should "throw exception" in {
recoverToSucceededIf[Exception] {
thriftRequest
}
}
}
回答by pme
Addition to Brian Low'sanswer, I found a nice explanation for recoverToSucceededIf. This is available in all Async styles(from ScalaTest 3):
除了Brian Low 的回答,我还找到了一个很好的解释recoverToSucceededIf。这适用于所有Async 样式(来自ScalaTest 3):
Failed futures can be tested in two ways: using recoverToSucceededIfor recoverToExceptionIf
可以通过两种方式测试失败的期货:使用recoverToSucceededIf或recoverToExceptionIf
recoverToSucceededIfis used for asserting the type of the exception the future ends in:
recoverToSucceededIf用于断言未来结束的异常类型:
"return UserNotFoundException" when {
"the user does not exist" in {
recoverToSucceededIf[UserNotFoundException](userService.findUser("1"))
}
}
recoverToExceptionIfis useful when you want to test some of the exception's fields:
recoverToExceptionIf当你想测试一些异常的字段时很有用:
"return UserAlreadyExistsException" when {
"adding a user with existing username" in {
recoverToExceptionIf[UserAlreadyExistsException] {
userService.addUser(user)
}.map { ex =>
ex.message shouldBe s"User with username: $username already exists!"
}
}
}
See the whole blog from Tudor Zgureanu — What's new in ScalaTest 3
回答by Ali
You can also try this Something Simple and Short
你也可以试试这个简单而简短的东西
test("some test throwing SQL Exception") {
val f: Future[Something] = someObject.giveMeAFuture
recoverToSucceededIf[SQLException](f)
}

