scala - scalatest - eventually trait wins over fail assertion? - scala

by documentation,
eventually trait
Invokes the passed by-name parameter repeatedly until it either
succeeds, or a configured maximum amount of time has passed, sleeping
a configured interval between attempts.
but fail,
fail to fail a test unconditionally;
so i want to use eventually in order to wait until a successful status arrived, but use fail to fail the test if i already know that the test must to fail
e.g.
converting a video with ffmpeg i will wait until conversion is not completed but if conversion reach "error" status i want to make the test fail
with this test
test("eventually fail") {
eventually (timeout(Span(30, Seconds)), interval(Span(15, Seconds))) {
println("Waiting... ")
assert(1==1)
fail("anyway you must fail")
}
}
i understand that i cannot make a test "fail unconditionally" inside eventually cicle : it looks like eventually will ignore "fail" until timeout.
is this a correct behaviour?

so, in the assertion scalatest documentation, fail should not "fail test unconditionally" but it "throw exception"?
It's the same because the only way to fail a test in Scalatest is to throw an exception.
Look at the source:
def eventually[T](fun: => T)(implicit config: PatienceConfig): T = {
val startNanos = System.nanoTime
def makeAValiantAttempt(): Either[Throwable, T] = {
try {
Right(fun)
}
catch {
case tpe: TestPendingException => throw tpe
case e: Throwable if !anExceptionThatShouldCauseAnAbort(e) => Left(e)
}
}
...
So if you want to get your failure through, you could use pending instead of fail (but of course, the test will be reported as pending, not failed). Or write your own version of eventually which lets more exceptions through.

Related

Future.successful(Failure(Exception)) vs just Future.failed()

I am trying to understand the difference why someone will use Future.successful(Failure(ServiceException("error"))) rather than Future.failed(ex) in Scala.
Future.successful(
Failure(
ServiceException(ErrorTypes.Validation, "Value should not be more than 30")
)
)
It's hard to say why the author did what they did, I can guess a couple of reasons:
They are not aware that you can Future.fail to return a failed future. This may happen because the signature of future doesn't convey the failure path explicitly in the type. When you see Future[A], you may think this computation must succeed in the future
They wanted to be explicit about failure. When I declare Future[Try[A]], I am forcing the caller to deal with the fact that can't directly access A, but have to "jump another hop" to get inside for A. Although this is delicate, as one can easily write:
val f: Future[Try[String]] = ???
f.map(_.map(str => // stuff with string))
But perhaps the signature forces the caller to realize the possibility of failure. Although Try is pretty limiting in it's expressiveness as well, as it can only return a Throwable on the failure path. I find Future[Either[E, A]] to be more expressive in that regards, which allows you to build your own failure hierarchy and encode it in the type.
There is an ever lasting debate regarding unary functor IO types (Future[A]) vs bi-functor IO types (IO[E, A], i.e. ZIO). I am in favor of bi-functor and have been using it solely recently, and I find it to be really time-saving when I don't have to dig into the implementation to understand the possible failures of a computation.
I would like to add on top of the great answer by #YuvalItzchakov, that writing code as in your question complicates the code and makes it unnatural. For example, let's assume that this future normally an Int, something like:
val succeeded = true // or any other computation
val future = if(succeeded) {
Future.successful(42)
} else {
Future.successful(Failure(new Exception("some failure text")))
}
Then first, the type of future is Future[Any], because Any is the lowest common root of Int and Failure, which is not convenient. But then, to map on it, you need to do:
future.map {
case i: Int =>
println(i)
case Failure(ex) =>
println(ex.getMessage)
} recover {
case NonFatal(ex) =>
println(s"recover ${ex.getMessage}")
}
Which feels weird to pattern match Int and Failure in the same match case. If you do want to pattern match Success and Failure, we had to do:
val succeeded = true // or any other computation
val future = if(succeeded) {
Future.successful(Success(42))
} else {
Future.successful(Failure(new Exception("some failure text")))
}
But that is already pretty clear as redundancy, right? No one will call Future.successful(Success(42)). So I think that it is pretty clear to use Future.failed(new Exception("some failure text")).

Scala, ZIO - how to test if effect returned success?

I have a simple test code which check mocked service:
runtime.unsafeRun(service.run.forkDaemon)
eventually(Timeout(Span(5, Seconds)), Interval(Span(1, Seconds))) {
verify(someMockResult).create(any[String], any[String])
}
The service returns Task[Done]. It works ok, but I would like to check also if this unsafeRun returns succeed. I tried to do it like this:
runtime.unsafeRun(service.run.forkDaemon) shouldBe succeed
or:
val result = runtime.unsafeRun(service.run.forkDaemon)
eventually(Timeout(Span(5, Seconds)), Interval(Span(1, Seconds))) {
result shouldBe succeed
}
But it does not work. Is there any way to check if effect result is succeed?
First of all, you misunderstood the meaning of org.scalatest.Succeed (which is aliased by succeed). It is needed only when you need to end up the body of a function with the Assertion type. It is equal to assert(true) basically. It is not an assertion that actually tests something.
If I understood right that you want to check the execution of your testTask that we define as:
val testTask: Task[Done] = service.run.forkDaemon
The problem in your code that val res = runtime.unsafeRun(testTask) executes it synchronously. This means that this line of code evaluates with two possible outcomes: 1) it successfully executes testTask and assigns the result to the res variable, or 2) execution of testTask fails, and it throws an exception.
So, basically, if there is no exception, it is succeeded.
Another way to check this more conveniently is to evaluate it to Future with runtime.unsafeRunToFuture and then assert that future is eventually succeeded. You can do it with, for example, like this:
assert(
future.value match {
case Some(Success(_)) => true
case _ => false
}
)

Unit Testing failures from Futures in Scala

I'm trying to test the error handling in a script I'm writing. If the async function fetchBar fails I pattern match the failure case and then return a successful future containing the failed result.
val fetchedBar = Try(fooClient.fetchBar(params))
fetchedBar match {
case Success(bar) => foobar(bar)
case Failure(e) => Future.successful(FooResult(success = false))
}
However when I unit test this flow I'm having trouble testing the failure case. I have stubbed fetchBar to return a failed future as shown below.
val fetchedBar = Try(Future.failed(new Exception()))
But I noticed that fetchedBar returns a Success rather than a Failure. Why is that and how can I stub the fetchBar function to create a failed Try?
I think you're slightly mixing concepts - but that's not 100% your fault.
The thing is, Future in Scala is a bit non-orthogonal concept - that it is, it represents not only the notion of delayed execution, but also a notion of failure.
Because of this, in most cases it doesn't make much sense to wrap the Future into the Try, or vice versa - unless one wants to explicitly separate the notion of failure from the notion of asynchrony.
In other words, the following combinations are sort of weird, but still has their use:
Try[Future[_]] - future already captures failures. However, makes sense if you have a (bad-behaving) library method that normally returns a Future, but may throw on a "synchronous" path:
def futureReciprocal(i: Int): Float = {
val reciprocal = 1 / i // Division by zero is intentional
Future.successful(reciprocal)
}
futureReciprocal(0) // throws
Try(futureReciprocal(0)) // Failure(DivisionByZero(...))
... but this is basically a workaround to fix poorly implemented function
Future[Try[_]] - sometimes useful to separate the "business" error (represented by Future.success(Failure(...))) from "infrastructure" failure (represented by Future.failed(...)). On a side - this is especially useful with akka-streams, which tend to treat failed futures as "fatal" to the stream.
In your case, what you want to do is to assert on the result of the future. To do so, you have at least two options, actually.
Block till the future is completed and check the outcome - this is usually done with scala.concurrent.Await:
// writing this without the compiler, might mix up namespaces a bit
import scala.concurrent.Await
import scala.concurrent.duration.DurationInt
val future = fooClient.fetchBar(...)
val futureResult: Try[_] = Await.result(future, 1.second)
futureResult match { case Success(_) => ??? ; case Failure(exc) => ???; }
Use some test framework that supports working with futures - e.g. scalatest:
class YourTest extends FlatSpec with ScalaFutures {
"fetchBar should return failed future" in {
val future: Future[XYZ] = fooClient.fetchBar(...)
// whenReady comes from the ScalaFutures trait
whenReady(future) { result => result shouldBe XYZ } // asserting on the successful future result
whenReady(future.failed) { exc => exc shoulBe a[RuntimeException] } // asserting on an exception in the failed future
}
}

Retrying Monix Task - why Task.defer is required here?

I recently spotted a case I can't fully understand while working with Monix Task:
There are two functions (in queue msg handler):
def handle(msg: RollbackMsg): Task[Unit] = {
logger.info(s"Attempting to rollback transaction ${msg.lockId}")
Task.defer(doRollback(msg)).onErrorRestart(5).foreachL { _ =>
logger.info(s"Transaction ${msg.lockId} rolled back")
}
}
private def doRollback(msg: RollbackMsg): Task[Unit] =
(for {
originalLock <- findOrigLock(msg.lockId)
existingClearanceOpt <- findExistingClearance(originalLock)
_ <- clearLock(originalLock, existingClearanceOpt)
} yield ()).transact(xa)
The internals of doRollback's for-comprehension are all a set of doobie calls returning ConnectionIO[_] monad and then transact is run on it turning the composition into Monix Task.
Now, as seen in handle function I'd like entire process to retry 5 times in case of failure. The mysterious part is that this simple call:
doRollback(msg).onErrorRestart(5)
doesn't really restart the operation on exception (verified in tests). In order to get this retry behaviour I have to explicitly wrap it in Task.defer, or have it already within Task "context" in any other way.
And this is the point I don't fully get: why is it so? doRollback already gives me Task instance, so I should be able to call onErrorRestart on it, no? If it's not the case how can I be sure that a Task instance i get from "somewhere" is ok to be restarted or not?
What am I missing here?

Can't seem to get Future to run callback in Scala

(I've not included the imports so as not to clutter this question)
(This is the simplest possible Scala App (created using scala-minimal template on Typesafe Activator))
I'm trying to run a query against an Elasticsearch Server.
I've run the same code on sbt console and I can see the results alright.
However, when I run the following code, I see "END" (code after the callbacks) being printed, but neither the Success callback nor the Failure callback get run.
I'm a Scala noob, so maybe I'm doing something wrong here? This code compiles. (Just to let you know all the imports are there)
object Hello{
def main(args: Array[String]): Unit = {
val client = ElasticClient.remote("vm-3bsa", 9300)
val res:Future[SearchResponse] = client.execute{ search in "vulnerabilities/3bsa" query "css" }
res onComplete{
case Success(s) => println(s)
case Failure(t) => println("An error has occured: " + t)
}
println("END")
//EDIT start
Await.result(res,10.seconds)
//EDIT end
}
}
FINAL EDIT
Instead of using onComplete, it works if I, instead, print result of the call to Await.result:
val await=Await.result(res,10.seconds)
println(await)
// results shown
The main thread will register your onComplete, println("END") and then exit, this makes the program terminate so therefore you never see your onComplete callback.
You can use Await.result(future, timeout) to block the main thread to keep it alive until the answer arrives. In a server context that would be a big no-no but in a small app like this it is not a problem blocking one thread.