Re: [akka-user] Is context.parent untestable?

808 views
Skip to first unread message

Roland Kuhn

unread,
Jan 25, 2013, 2:31:15 PM1/25/13
to akka...@googlegroups.com
Hi Bryan,

just fabricate a parent:

val parent = system.actorOf(Props(new Actor {
  val child = context.actorOf(Props[Child], "child")
  def receive = {
    case x if sender == child => testActor forward x
    case x => child forward x
  }
}

Then you can test it as usual:

parent ! 'ping
expectMsg('pong)
lastSender.path.name must be("child")

Regards,

Roland

25 jan 2013 kl. 19:54 skrev Bryan Hunt:

Hi all, I'm trying to build up a decent parent-child pattern. I'm testing using Akka-Testkit.
The simplification follows:

 import reflect.ClassManifest
 
class Parent[T <: Child : ClassManifest] extends Actor {
  val child = context.actorOf(Props[T])
 
  def receive = {
    case 'pong => println("got pong")
    case 'ping_child => child ! 'ping
    case a => println(a)
  }
}
 
class Child extends Actor {
  def receive = {
    case 'ping => context.parent ! 'pong
    case a => println(a)
  }
}
 

Is there a way in which I can test the Child, in isolation, using TestKit?

I've tried a lot of things, but can't seem to get messages to the testActor.

Best Regards,

Bryan Hunt





--
>>>>>>>>>> Read the docs: http://akka.io/docs/
>>>>>>>>>> Check the FAQ: http://akka.io/faq/
>>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
---
You received this message because you are subscribed to the Google Groups "Akka User List" group.
To post to this group, send email to akka...@googlegroups.com.
To unsubscribe from this group, send email to akka-user+...@googlegroups.com.
Visit this group at http://groups.google.com/group/akka-user?hl=en.
 
 



Dr. Roland Kuhn
Akka Tech Lead
Typesafe – The software stack for applications that scale.
twitter: @rolandkuhn

Roland Kuhn

unread,
Jan 28, 2013, 6:35:53 AM1/28/13
to akka...@googlegroups.com
Hi Bryan,

I’m not sure a generic thing would be worthwhile, you’d have to do your own again as soon as e.g. supervisorStrategy is involved; given how little work it is to roll your own I’m leaning towards only documenting it. That has the added benefit that understanding the test does not include knowledge about how exactly this supervisor factory works.

Regards,

Roland

27 jan 2013 kl. 17:13 skrev Bryan Hunt:

Thanks Roland, that trick is rather handy.
Perhaps the TestKit maintainers could add a similar method to the API.
I'm pasting in the full example so that it may be useful to anyone doing something similar.

import akka.actor._
import akka.actor.FSM
import akka.testkit._
import org.specs2.mock.Mockito
import org.specs2.mutable.Specification

class ChildParentFSMTest extends TestKit(_system = ActorSystem()) with Specification with Mockito with DefaultTimeout with ImplicitSender {

  // FTP States
  sealed trait FTPState

  case object FTPUninitialized extends FTPState

  case object FTPIdle extends FTPState

  case object FTPSyncing extends FTPState

  // FTP Events
  sealed trait FTPEvent

  case class FTPDoInit(conf: FTPConf) extends FTPEvent

  // FTP Data
  sealed trait FTPData

  case object FTPNone extends FTPData

  case class FTPInitialized(downloader: FTPDownloader, conf: FTPConf) extends FTPData

  case class FTPSynced(downloader: FTPDownloader, conf: FTPConf, newFiles: Option[List[_]] = None,
                       oldFiles: Option[List[_]] = None) extends FTPData

  class FTPConf(val remoteDir: String = "/") {}

  class FTPDownloader(val conf: FTPConf) {
    def open() = ()
    def changeDir(s: String) = ()
  }

  "FTPFetcher should " in {
    "Verify Parent - Child communications" in {
      class FTPFsm extends Actor with FSM[FTPState, FTPData] {
        startWith(FTPUninitialized, FTPNone)

        when(FTPUninitialized) {
          case Event(FTPDoInit(c), FTPNone) => {
            println("FTPDoInit")
            goto(FTPIdle) using FTPInitialized(new FTPDownloader(c), c)
          }
        }

        when(FTPIdle) {
          case Event('checkfiles, input@FTPInitialized(ftp, c)) => goto(FTPIdle) using input
        }

        onTransition {
          case FTPUninitialized -> FTPIdle =>
            stateData match {
              case _ => {
                print("replying!")
                context.parent ! 'connected
              }
            }
        }
      }

      val p = system.actorOf(Props(new Actor {
        val child = context.actorOf(Props(new FTPFsm), "child")


        def receive = {
          case x if sender == child => testActor forward x
          case x => child forward x
        }
      }))

      p ! FTPDoInit(new FTPConf)
      p ! 'checkfiles
      expectMsg('connected)
      success

    }
  }
}


--
>>>>>>>>>> Read the docs: http://akka.io/docs/
>>>>>>>>>> Check the FAQ: http://akka.io/faq/
>>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
---
You received this message because you are subscribed to the Google Groups "Akka User List" group.
To post to this group, send email to akka...@googlegroups.com.
To unsubscribe from this group, send email to akka-user+...@googlegroups.com.
Visit this group at http://groups.google.com/group/akka-user?hl=en.
 
 

Roland Kuhn

unread,
Jan 28, 2013, 7:38:53 AM1/28/13
to akka...@googlegroups.com
Hi Bryan,

28 jan 2013 kl. 12:48 skrev Bryan Hunt:

HI Roland,

That reply really helped me out. Just one other question, I've been working a lot with TestFSMRef, and I can emulate it's behaviour using the solution you provided.

I keep going back to TestFSMRef though, and trying to figure out a way of using it to create a FSM Actor Ref, but setting the parent to testActor.

It would make my code a lot less verbose if I could figure out how to do that, but I don't think it's possible given the API.

I have been trying to set it up in the following fashion, but without any success.

      var fsm: FTPFsm = _

This is not needed: you get the same by calling t.underlyingActor.


      val t = TestFSMRef({
        fsm = new FTPFsm() ; fsm
      })

      t ! FTPDoInit(new FTPConf)
      t ! 'checkfiles
      expectMsg('connected)

I don’t see what you intend to do here, testActor does not appear in this code?

assertion failed: timeout (3 seconds) during expectMsg while waiting for 'connected

Is this a waste of time or am I misunderstanding the Testkit API?

You should use the mocked-up parent approach, you can then factor that out into a method within your tests to avoid duplication.

Regards,

Roland Kuhn

unread,
Jan 28, 2013, 9:44:38 AM1/28/13
to akka...@googlegroups.com
28 jan 2013 kl. 15:37 skrev Bryan Hunt:

      val t = TestFSMRef({
        fsm = new FTPFsm() ; fsm
      })

      t ! FTPDoInit(new FTPConf)
      t ! 'checkfiles
      expectMsg('connected)

I don’t see what you intend to do here, testActor does not appear in this code?

Yes, I couldn't figure out how to set testActor as the parent of TestFSMRef.

I guess what I was looking for was a way to do the equivalent of:

      val t = TestFSMRef(new FTPFsm(), parent=testActor)  //This apply method doesn't exist, but it would be nice if it did.

      t ! FTPDoInit(new FTPConf)
      t ! 'checkfiles
      expectMsg('connected)

I don't have a huge experience with the Akka API so I just wanted to make sure I wasn't missing something basic.

Ah, now I see. No, that method does not exist, because in general it is quite hideous to attach children to unaware parents, don’t you think?

Having said that: on TestActorRef’s companion there is def apply[T <: Actor](props: Props, supervisor: ActorRef, name: String)(implicit system: ActorSystem): TestActorRef[T]

But you didn’t hear that from me ;-) (meaning that that method has very limited applicability in terms of not messing up things; in a controlled test environment it might just be what you need, though)

Regards,

Roland


Regards,

Bryan Hunt




--
>>>>>>>>>> Read the docs: http://akka.io/docs/
>>>>>>>>>> Check the FAQ: http://akka.io/faq/
>>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
---
You received this message because you are subscribed to the Google Groups "Akka User List" group.
To post to this group, send email to akka...@googlegroups.com.
To unsubscribe from this group, send email to akka-user+...@googlegroups.com.
Visit this group at http://groups.google.com/group/akka-user?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 
Reply all
Reply to author
Forward
0 new messages