Scala Test - Code with Await ignore the mocked future - scala

I'm writing unit tests for a service method that returns a Future[Boolean].
Inside the code that I'm testing, there is an Await.result usage on a sequence of future.
class serviceLogic, method logic: Future[Boolean] -
val futureList: List[Future[Model]] = iterator.map { item =>
val handleResult: Future[Model] = handlerService.handle(item)
// do some logic
handleResult
}.toList
Await.result(Future.sequence(futureList), 1 minute)
// some logic
In the unit test that I'm writing, I'm mocking the handlerService to return a Future.successful for the handle method and I do see that it returns a successful result, but in the test, the Await.result waits till the duration ends and it ignores the mock of the calls, as the future didn't finish.
While having the same test that returns a Future.failed(new RuntimeException), this is working as expected and the Await.result returns immediately
The test with the Future failed that works as expected:
"Some operation" should "should fail if handle failed" {
val someModel = Model()
when(handlerServiceMock).handle(anyString())
.thenReturn(Future.failed(new RuntimeException))
serviceLogic.logic().map { result =>
result mustBe false
}
}
But, this is not working for a successful future. the test looks like that:
"Some operation" should "should succeed if handle succeeded" {
val someModel = Model()
when(handlerServiceMock).handle(anyString())
.thenReturn(Future.successful(someModel))
serviceLogic.logic().map { result =>
result mustBe true
}
}
The test fails as it waits for the duration.
I do see in debug that for each handle the return is as expected Future(Success(model)), but the Future.sequence(futureList) is Future(not completed), so this is not working properly or I'm missing something...
Can you please help?

Related

How to set a timeout for Async suite in Scalatest?

Consider the following unit test example:
class MySpec extends AsyncFlatSpec {
"this" should "fail after some timeout" in {
val resultFuture: Future[_] = Promise().future
for (result <- resultFuture) yield {
assert(result == ???) // some assertions
assert(result == ???) // some assertions
assert(result == ???) // some assertions
}
}
}
The problem
If resultFuture never completes the test suite never finishes either.
As I understand it's due to the way how SerialExecutionContext is implemented.
The question
Is there any "nice" way how to setup a timeout for this kind of tests, so that if the future isn't complete the test just fails, instead of blocking the entire test suite for eternity?
UPDATE and clarification
While solutions https://stackoverflow.com/a/65746143/96766 and https://stackoverflow.com/a/65749840/96766 (posted by #matthias-berndt and #tomer-shetah) work for the case of blocked thread, it's not exactly what I'm looking for.
Let me make an important clarification to the question. In my case the future isn't eventually complete, but never complete. For example, when a Future is obtained from the Promise that is never resolved (nobody calls success nor failure on it). In that case the proposed solutions still block infinitely.
Is there a way to work this around for AsyncSpec without resorting to using a real pool-based execution context and Await-ing on the future?
Use eventually from scalatest
extends Eventually
Use the following code to set up timeout and interval for checking
import scala.concurrent.duration._
eventually(timeout(1 minutes), interval(5 seconds)) {
resultFuture.futureValue shouldBe ???
}
Use the AsyncTimeLimitedTests trait.
https://www.scalatest.org/scaladoc/3.2.0/org/scalatest/concurrent/AsyncTimeLimitedTests.html
You can use the trait TimeLimits. For example, you can have a test class:
class MySpec extends AsyncFlatSpec with TimeLimits {
"this" should "fail" in {
failAfter(2.seconds) {
val resultFuture: Future[_] = Future {
Thread.sleep(3000)
}
assert(true)
}
}
"this" should "pass" in {
failAfter(2.seconds) {
val resultFuture: Future[_] = Future {
Thread.sleep(1000)
}
assert(true)
}
}
}

Exception not getting handled in recover method

This method throws exception
#throws(classOf[QuestionNotFoundException])
def getQuestionFromQuestionID(questionKey: PracticeQuestionKeys) = {
logger.trace(s"getting question with keys ${questionKey}")
val practiceQuestionFuture: Future[Option[PracticeQuestion]] = findOne(questionKey)
for (questionOption <- practiceQuestionFuture) yield {
questionOption.fold(throw QuestionNotFoundException())(question => {
logger.trace("got question " + question)
question
})
}
}
I use it like follows
def function1 = {
...
val res = for{existingQuestion <- questionsRepository.getQuestionFromQuestionID(questionKey){..}
res.recover {
case exception => {
logger.trace(s"exception ${exception}")
Ok(Json.toJson(JsonResultError(messagesApi("error.answerAdditionFail")(langs.availables(0))+". "+exception.getMessage())))
}
...
}
I want to test that function1 handles the thrown exception. I have written the following test case
"function" should {
"should return error if the question id is not correct" in {
...
when(answerTestEnv.mockPracticeQuestionsRepository.getQuestionFromQuestionID(ArgumentMatchers.any[PracticeQuestionKeys])).thenThrow(
QuestionNotFoundException()
)
val response:Accumulator[ByteString,Result] = controller.function1(request)
//note that run is how accumulator gets the data and starts processing it. no parameter means no data
val resultOfAccumulatorRun:Future[Result] = response.run(ByteString(body))
val responseBody = contentAsJson(resultOfAccumulatorRun)(Timeout(Duration(5000,"millis")),TestEnvImplicits.mat)
println(s"received response ${responseBody}")
val result = (responseBody \ "result").get.as[String]
val additionalInfo = (responseBody \ "additional-info").get.as[String]
result mustBe "error"
additionalInfo mustBe components.messagesApi("error.answerAdditionFail")(components.langs.availables(0))+". "+QuestionNotFoundException().msg
}
}
But when I run my test case, the Exception gets thrown but it seems that res.recover doesn't handle it. I see this in the console.
Question not found
utilities.QuestionNotFoundException: Question not found
at controllers.AnswerController.$anonfun$newAnswer$1(AnswerController.scala:428)
Why doestres.recover` doesn't handle it.
the partial function passed to recover() gets invoked on a failed Future, but your Future isn't a failed Future, it is a Future that contains an exception as its 'successful' result type (actually it will be forced to be something more like Future[AnyRef], as it could be either an Exception or a PracticeQuestion).
You probably don't want to be using Option.fold() like that. Instead try:
practiceQuestionFuture.flatMap { qOpt =>
qOpt.map(q => Future(q))
.getOrElse(Future.failed(QuestionNotFoundException()))
}
(NB: I'm not near a compiler to try this out right now, so I may have made a syntactical mistake or two, but there should be enough there to get you going).

scala: playframework controller action execution sequence with future

I am having problem with my play-framework API server. I need to have some processing running in the background that is returning a Future with the result and then write the result as response. However, the request thread goes all out and return before my Future completes. Here is the code...
def requestAction(): Action[AnyContent] = Action.async { implicit request =>
var fResult: Future[String] = Future { "initial value" }
try {
fResult = doSomethingAsyncAndGetResponseString(); // return "great, everything is done"
}
catch {
case t: Throwable {
fResult = Future { "failed" }
}
}
// done, return response, but the problem is, this is executed first because doSomethingAsyncAndGetResponseString() is still executing and returns later
fResult.map( res => {
// problem here, because I get "initial value" which is not what I want
Ok(res)
}
}
Is there a way to get "great, everything is done" or "failed" without Async.await ? I have been using this format all over in my API server, but today it broke because in a new API that I write, the doSomethingAsyncAndGetResponseString is a bit longer. I didn't expect that, so something must be wrong with how I understand the structure.
Thanks in advance!
You are trying to write Java like code using Scala.
You are doing it wrong. Read about Futures and How to use them.
Here is the tutorial
Futures can be composed using map and flatMap constructs. recover and recoverWith will give the user access to exception happened in the computation pipeline
You have to do something like this, given below
def requestAction(): Action[AnyContent] = Action.async { implicit request =>
Future { "initial value" }.flatMap { _ =>
doSomethingAsyncAndGetResponseString() // returns Future
}.map { res =>
Ok(res)
}.recover { case th =>
Ok(th.getMessage)
}
}
Handling exceptions and recovering from exceptions
Exception handling is inbuilt into Future.
recover gives access to exception and also helps the user to provide the alternative success value in case of exception.
recoverWith gives access to an exception and also helps the user to provide/chain alternative Future computation which can succeed or fail.
Future {
throw new Exception("foo exception")
}.recover {
case th => println(s"msg: ${th.getMessage}")
}

Failed to call Future#onSuccess [duplicate]

Just trying to get my first futures use up and running and doing a test similar to an example outlined in the Akka in Action MEAP book. I want to call a web service and return the result in a future. I am using scalaxb to access the web service. I have outlined the code below but when I run it the app terminates without waiting for the response from the service. Maybe someone can tell me what I am missing?
import scala.util._
import control.NonFatal
import scala.concurrent._
import ExecutionContext.Implicits.global
object Test {
val service = (new MyServiceBindings with scalaxb.Soap11Clients with scalaxb.DispatchHttpClients {}).service
def test = {
val f = future {
service.someCall() match {
case Right(resp) => resp
case Left(fault) => throw new Exception("Fault: " + fault)}
}
}
f.onComplete {
case Success(resp) => println("Resp: " + resp)
case Failure(NonFatal(e)) => println("Fail: " + e)
}
}
def main(args: Array[String]): Unit = {
test
}
}
It terminates because the main thread that is executed inside your Test has completed. The threads that are used internally by the Dispatch library don't keep the program from exiting.
You would need to wait on the future since this is the only thing your test app is doing. Put this after the onComplete statement.
import scala.concurrent.duration._
Await.ready(f, 10.seconds)
Now bear in mind that this is bad practice usually. You need it here because your test app is doing nothing else, but in a real app, you wouldn't want to block after each futures call, as that would negate the point of using futures.
You can patch this in the main funtion by adding the following logic to your test application:
def main(args: Array[String]) : Unit = {
// Assuming test is a future that did not complete yet.
while(!test.isCompleted) {
Thread.sleep(100)
}
// Software exits here only after future is completed.
}
Or Better if you have many futures you can implement it this way:
// Assuming you have --> listOfFutures = ArrayBuffer[Future]
def main(args: Array[String]) : Unit = {
while(!listOfFutures.isEmpty) {
listOfFutures.foreach {
future =>
if(future.isCompleted) {
listOfFutures -= future
}
}
//checks which futures are completed every half-a-second
Thread.sleep(500)
}
// Program exits after all futures have been completed.
}

Scala future app terminates before complete

Just trying to get my first futures use up and running and doing a test similar to an example outlined in the Akka in Action MEAP book. I want to call a web service and return the result in a future. I am using scalaxb to access the web service. I have outlined the code below but when I run it the app terminates without waiting for the response from the service. Maybe someone can tell me what I am missing?
import scala.util._
import control.NonFatal
import scala.concurrent._
import ExecutionContext.Implicits.global
object Test {
val service = (new MyServiceBindings with scalaxb.Soap11Clients with scalaxb.DispatchHttpClients {}).service
def test = {
val f = future {
service.someCall() match {
case Right(resp) => resp
case Left(fault) => throw new Exception("Fault: " + fault)}
}
}
f.onComplete {
case Success(resp) => println("Resp: " + resp)
case Failure(NonFatal(e)) => println("Fail: " + e)
}
}
def main(args: Array[String]): Unit = {
test
}
}
It terminates because the main thread that is executed inside your Test has completed. The threads that are used internally by the Dispatch library don't keep the program from exiting.
You would need to wait on the future since this is the only thing your test app is doing. Put this after the onComplete statement.
import scala.concurrent.duration._
Await.ready(f, 10.seconds)
Now bear in mind that this is bad practice usually. You need it here because your test app is doing nothing else, but in a real app, you wouldn't want to block after each futures call, as that would negate the point of using futures.
You can patch this in the main funtion by adding the following logic to your test application:
def main(args: Array[String]) : Unit = {
// Assuming test is a future that did not complete yet.
while(!test.isCompleted) {
Thread.sleep(100)
}
// Software exits here only after future is completed.
}
Or Better if you have many futures you can implement it this way:
// Assuming you have --> listOfFutures = ArrayBuffer[Future]
def main(args: Array[String]) : Unit = {
while(!listOfFutures.isEmpty) {
listOfFutures.foreach {
future =>
if(future.isCompleted) {
listOfFutures -= future
}
}
//checks which futures are completed every half-a-second
Thread.sleep(500)
}
// Program exits after all futures have been completed.
}