Akka actors unit testing with Scala - scala

I'm fairly new to Scala so please be gentle.
In the app I'm currently building, I'm using Akka actors and I want to write some unit tests. I came across this official documentation for writing unit tests for Akka actors
but I couldn't understand exactly how it should work. In particular,
val actorRef = TestActorRef(new MyActor)
// hypothetical message stimulating a '42' answer
val future = actorRef ? Say42
val Success(result: Int) = future.value.get
result must be(42)
When I try that, I get not found: value Success, which is not surprising.
I then found this example of how to test Scala actors
val actorRef = TestActorRef[TickTock]
implicit val timeout = Timeout(5 seconds)
val future = (actorRef ? new Tick("msg")).mapTo[String]
val result = Await.result(future, timeout.duration)
Assert.assertEquals("processed the tick message", result)
, which admittedly is possibly old, but it is easy to understand and closer to what I normally use when I want to use Futures, and most importantly works. It does require me to declare a few implicits like the ActorSystem, timeout and such, which doesn't seem to be the case with the official way...
If possible, I'd like to use the method proposed by the official documentation, so I would appreciate it if someone could help me understand how it works (in particular the Success bit) and how to use it.

The answer to your question might be too long, because it is impossible to know how much Scala you actually know. I will try to make my answer as short as possible, but do not hesitate to ask for clarification at any point. I also apologize on behalf of the whole stackoverflow community for making you feel the need to apologize due to an apparent lack of skill before asking a question.
In Scala 2.10 a concept of Try was introduced. It is very similar to Option. Option is a concept of handling nulls. A value of type Option can take two forms: Some(value) or None. When you have an Optional value you can pattern match on it to see if it is a Some or a None and then act accordingly. Pattern matching occurs in many places in Scala and one of them is during the initialization of vals. Here are few examples:
val x = 10 // pattern 'x' on the LHS matches any value on the RHS so 'x' is initialized with 10
val Some(x) = Some(10) // pattern 'Some(x)' on the LHS matches any value of type 'Some' and binds it's value to x, so 'x' is yet again initialized with 10
Try is a concept of handling exceptions. A value of type Try can take two forms: Success(result) or Failure(throwable). When you have a value of type Try you can pattern match on it to see if it is a Success or a Failure.
This is what happens in your code (pattern matching on Success). In contrast to Option the two forms of Try are not in scope by default, which causes the compilation error. This will fix it:
import scala.util.{Try, Success, Failure}

Have your test extend the TestKit and then add "with ImplicitSender" and then you can do things like:
val yourActor = system.actorOf(Props[MyActor])
yourActor ! Say42
expectMsg(42)

Firstly it's not a good pattern to use get on futures value, this can raise an exception if there was a failure. You should use either Await.result, like in your seconds example, or use pattern matching to work with Success and Failure:
future match {
case Success(value) => // work with value
case Failure(ex) => // work with exception
}
to use Success and Failure import scala.util._ or scala.util.{Success, Failure}
Here is an official documentation for the latest release 2.2-M3.

Related

Lifting A Future using EitherT.liftF When Value Is Already a Future

I have a value like this:
val ss: Option[Future[List[Either[Error, File]]]]
And what I want to do is to lift this to an EitherT.liftF[Future, Error, List[Either[Error, File]]] so what I did was this:
ss match {
case Some(value) => EitherT.liftF[Future, Error, List[Either[Error, File]]](value)
case None => EitherT.leftT[Future, List[Either[Error, File]]](Error("failed"))
}
My question is whether it is correct that I can use EitherT.liftF to lift a value which is already a future because I think normally that is used for values which need to be lifted to a future, not one which is a future itself.
Future has a Monad instance for practical reasons but isn't really lawful. See this post on the topic.
However it will still behave as expected in many cases such as yours.
You can also implement it this way:
EitherT(ss.toRight(Error("failed")).sequence)

Convert try to option without losing error information in Scala

Introduction
I had created a lovely one-liner:
Option("something").map(_ => Try("something else")).flatten.getOrElse("default")
which actually does not compile, with error:
Error:(15, 31) Cannot prove that scala.util.Try[String] <:< Option[B].
Option("").map(_ => Try("")).flatten.getOrElse("");}
^
so I had found a way around:
Option("something").flatMap(_ => Try("something else").toOption).getOrElse("default")
But, the problem
My colleague warned me, that my construction is actually loosing the error information. This is true, and in real-life application - not acceptable.
After getting rid of all the repetition I had ended up with:
implicit class CoolTry[T](t: Try[T]) extends StrictLogging {
def toOptionSE: Option[T] = t match {
case Success(s) => Some(s)
case Failure(ex) =>
logger.error(ex.getMessage, ex)
None
}
}
using:
Option("something").flatMap(_ => Try(new Exception("error")).toOptionSE).getOrElse("default")
Question
I believe there are many similar cases in every application and I simply do not know if either my approach is bad or Try().toOption is simply done wrong?
I understand that logging is a side effect, but while using Try I guess everyone does expect it if something goes wrong?
Can the implicit class be improved?
Is there any utility library handling Try().toOption my way?
Or what (other) approach should I take here?
Thanks!
Forcing logging every time you change a Try[T] to an Option[T] is an undesired effect IMO. When you do such a transformation you're explicitly admitting that you don't really care about the internals of the failure, if it happened. All you want is to access the result, if it exists. Most of the time you say "well, this is undesirable, I always want to log exceptions", but sometimes you simply only care about the end result, so dealing with an Option[T] can be good enough.
Or what (other) approach should I take here?
Using Either[A, B] is an option here, especially in Scala 2.12 when it became right biased. You can simply map over it and only at the end check if there was an error and then log (created with Scala 2.12.1):
val res: Either[Throwable, String] = Option("something")
.map(_ => Try("something else").toEither)
.getOrElse(Right("default"))
.map(str => s"I got my awesome $str")
Ideally (IMO) deferring the logging side effect to the last possible point would serve you better.
One possible improvement would be to make it crystal clear to the user of what is going on; between the implicit being injected somewhere by the compiler and the apparently "pure" name (toOptionSE) it may not be evident of what is going on for a second developer reading and/or modifying your code. Furthermore, you're fixing how you treat the error case and don't leave the opportunity to handle it differently from logging it.
You can treat errors by leveraging projection, like the failed projection defined over Try. If you really want to do this fluently and on one line, you can leverage implicit classes like this.
implicit class TryErrorHandlingForwarding[A](t: Try[A]) {
def onError(handler: Throwable => Unit): Try[A] = {
t.failed.foreach(handler)
t
}
}
// maybe here you want to have an actual logger
def printStackTrace: Throwable => Unit =
_.printStackTrace
Option("something").
flatMap(_ => Try(???).onError(printStackTrace).toOption).
getOrElse("default")
Also, here I'm assuming that for whatever reason you cannot use Try right from the start (as it's been suggested in a comment).

Future[Option[Future[Int]]] to Future[Option[Int]]

What is the cleanest way in Scala to convert from Future[Option[Future[Int]]] to Future[Option[Int]]? Is it even possible?
There are two nested Futures that you roll into one, so this calls for a flatMap.
def flatten[A](f: Future[Option[Future[A]]]): Future[Option[A]] =
f.flatMap({
case None => Future.successful(None)
case Some(g) => g.map(Some(_))
})
Or, more concisely:
def flatten[A](f: Future[Option[Future[A]]]): Future[Option[A]] =
f.flatMap(o => Future.sequence(o.toSeq).map(_.headOption))
Thanks to #lmm's answer for the idea on that. Ideally this could be written as f.flatMap(Future.sequence), but unfortunately sequence expects a TraversableOnce, which Option does not extend.
The cleanest way is probably using scalaz:
import scalaz.std.option._
import scalaz.std.scalaFuture._
import scalaz.syntax.traverse._
//assuming implicit execution context in scope
f.flatMap(_.sequence)
You are asking how to code your way out of some paper bag, and you have gotten some good answers about how to do this, however you should be stepping back and trying to just not get into the paper bag you are currently in. See my answer here: Get rid of Scala Future nesting about how to avoid getting into a Future[X[Future[y]] type signature so that you don't have to figure out how to then get out of it.

How to interpret a val in Scala that is of type Option[T]

I am trying to analyze Scala code written by someone else, and in doing so, I would like to be able to write Unit Tests (that were not written before the code was written, unfortunately).
Being a relative Newbie to Scala, especially in the Futures concept area, I am trying to understand the following line of code.
val niceAnalysis:Option[(niceReport) => Future[niceReport]] = None
Update:
The above line of code should be:
val niceAnalysis:Option[(NiceReport) => Future[NiceReport]] = None
- Where NiceReport is a case class
-----------Update ends here----------------
Since I am trying to mock up an Actor, I created this new Actor where I introduce my niceAnalysis val as a field.
The first problem I see with this "niceAnalysis" thing is that it looks like an anonymous function.
How do I "initialize" this val, or to give it an initial value.
My goal is to create a test in my test class, where I am going to pass in this initialized val value into my test actor's receive method.
My naive approach to accomplish this looked like:
val myActorUnderTestRef = TestActorRef(new MyActorUnderTest("None))
Neither does IntelliJ like it. My SBT compile and test fails.
So, I need to understand the "niceAnalyis" declaration first and then understand how to give it an initial value. Please advise.
You are correct that this is a value that might contain a function from type niceReport to Future[niceReport]. You can pass an anonymous function or just a function pointer. The easiest to understand might be the pointer, so I will provide that first, but the easiest in longer terms would be the anonymous function most likely, which I will show second:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
def strToFuture(x: String) = Future{ x } //merely wrap the string in a future
val foo = Option(strToFuture)
Conversely, the one liner is as follows:
val foo = Option((x:String)=>Future{x})

Does Scalaz have something to accumulate in both error and success?

I started to use Scalaz 7 Validation and/or disjunction to process a list of possibly failing operation and managing their result.
There is two well documented case for that kind of use cases:
1/ You want to check a list of conditions on something, and accumulate each error if any. Here, you always go the end of list, and in case of any error, you have failure as global result.
And that's an applicative functor at work.
2/ You want to execute several steps that may fail, and stop on the first one failing.
Here, we have a monad that goes nicely in Scala for-comprehension.
So, I have two other use cases that are among the same lines, but don't seems to feet well on any precedent case:
I want to process a list of step, possibly failing, and accumulate both error and success results (ex: it's a list of modification on files, errors may happen because that's the outer world, and success are patch that I want to keep for later).
The difference on the two use case is only if I want to stop early (on the first error) or go to the end of the list.
OK, so what is the correct thing for that ?
(writting the question leads me to think that it's just a simple foldLeft, does it ? I will let the question here to validate, and if anybody else wonder)
Take a look at Validation#append or its alias Validation#+|+. Given two validations, if both are success, it returns success of the values appended. If both are failures, it returns failure of the values appended. Otherwise, it returns the successful value. This requires an implicit Semigroup instance for the success type.
I'd do something like this:
scala> List(1.success[String], 2.success[String], "3".failure[Int], "4".failure[Int]).partition(_.isSuccess)
res2: (List[scalaz.Validation[java.lang.String,Int]], List[scalaz.Validation[java.lang.String,Int]]) = (List(Success(1), Success(2)),List(Failure(3), Failure(4)))
scala> val fun = (_:List[Validation[String, Int]]).reduceLeft(_ append _)
fun: List[scalaz.Validation[String,Int]] => scalaz.Validation[String,Int] = <function1>
scala> fun <-: res2 :-> fun
res3: (scalaz.Validation[String,Int], scalaz.Validation[String,Int]) = (Success(3),Failure(34))
UPD: With #129 and #130 merged, you can change fun to (_:List[Validation[String, Int]]).concatenate or (_:List[Validation[String, Int]]).suml
Or bimap like this:
scala> List(1.success[String], 2.success[String], "3".failure[Int], "4".failure[Int]).partition(_.isSuccess).bimap(_.suml, _.suml)
res6: (scalaz.Validation[java.lang.String,Int], scalaz.Validation[java.lang.String,Int]) = (Success(3),Failure(34))
What you need is approximately switching an Either[E, A] into a Writer[List[E], A]. The Writer monad logs the errors you encountered.
Sounds like you want a pair (SomveValue, List[T]) where T is your 'Failure' although I'd call it 'Warning' or 'Log' since you still get a result, so its not really a failure.
Don't know if Scalaz has anything fancy for this though.