ScalaTest: Assert exceptions in failed futures (non-blocking)

前端 未结 6 692
夕颜
夕颜 2020-12-24 10:54
import org.scalatest.{ FlatSpec, Matchers, ParallelTestExecution }
import org.scalatest.concurrent.ScalaFutures
import org.apache.thrift.TApplicationException

class         


        
6条回答
  •  清歌不尽
    2020-12-24 11:34

    Note: leaving this answer because the OP found it helpful, but for Scala Futures see the other answer.

    This is a bit boilerplated, but Waiter from 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.

提交回复
热议问题