9

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.

lloydmeta
  • 1,289
  • 1
  • 15
  • 25

3 Answers3

13

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}
agilesteel
  • 16,775
  • 6
  • 44
  • 55
  • Great analogy; I understand Pattern Matching and Options fairly well and was able to follow along. I just had no idea about Try, Success and Failure. I suppose then that Future.value.get returns a value of type Try then? In other words, the example in the following way: `val futureValue= future.value.get futureValue match { case Success(x:Int) => x must be(42) case _ => false must be(true) //just fail }` The above works for me in my tests:) – lloydmeta May 28 '13 at 10:18
  • 1
    Yes, exactly the same thing, but you definitively should go for the approach suggested by Viktor Klang. – agilesteel May 28 '13 at 10:18
  • Thanks, I'll accept your answer because it answered my question and cleared up my confusion about Success the most. – lloydmeta May 28 '13 at 10:20
5

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)
Viktor Klang
  • 26,479
  • 7
  • 51
  • 68
  • Thanks, this really helped. After some searching I found [this example](http://doc.akka.io/docs/akka/2.1.4/scala/testkit-example.html) demonstrating how to use ImplicitSender with DefaultTimeout, etc. – lloydmeta May 28 '13 at 10:28
4

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.

4lex1v
  • 21,367
  • 6
  • 52
  • 86