A Proposal for MockSpecializedLiftActor and MockLiftActor

50 views
Skip to first unread message

Matt Farmer

unread,
Jun 28, 2013, 9:59:17 AM6/28/13
to lif...@googlegroups.com
Hey folks,

I did some basic searching for "mock actor" across the list and the most I came up with were some instructions about mocking with Scala actors. I've been brainstorming the problem of using actors in testing and wanted to bounce some ideas off you guys and (potentially) get some +1's on a specification that I'd like to implement in the Lift Codebase.

All of the thought that I've put into this equation thus far is based on the following premise: If I'm writing a test for a snippet that should send a message to an actor, I should be able to test that the message got sent to the actor without having to test what the actor did with it because what an actor does with a message belongs in the tests for that actor, not my snippet. Currently, there's no way that I can see to implement this in Lift, but I think it's something we should put some heavy consideration toward doing because I think it'll make a lot of unit tests for a lot of people a lot cleaner, and result in the deduplication of a lot of code. No longer would you have to check the "result" of an actor processing a message to see if it actually got that message (which currently appears to be the only way to really include actors in test).

For the specification below, I'm assume the following basic structure for the snippet we want to test:

object MyAwesomeSnippet extends MyAwesomeSnippet {
  val myAwesomeActor
: LiftActor = MyAwesomeActor // My awesome actor is a singleton LiftActor.
}
trait
MyAwesomeSnippet {
 
def myAwesomeActor: LiftActor

 
def sendTheActorAMessage {
    myAwesomeActor
! SomeMessage
 
}
}

Ideally, when you're writing your tests, I could write a test like this:

"My Awesome Snippet" should {
  "Dispatch the SomeMessage message to the MyAwesomeActor" in {
    val myAwesomeSnippetMock = new MyAwesomeSnippet {
      val myAwesomeActor = new MockLiftActor()
    }

    myAwesomeSnippetMock.sendTheActorMessage

    myAwesomeSnippetMock.myAwesomeActor.messageCount must_== 1
    myAwesomeSnippetMock.myAwesomeActor.hasMessage_?(SomeMessage) must_== true
  }
}

Here, I'm only testing whether or not SomeMessage ends up in the inbox. Not what the real actor would do with that message when it receives it.

I think this could be accomplished through the implementation of a MockSpecializedLiftActor and a MockLiftActor that, instead of executing messages when they are passed in using ! (and others), it simply adds them to a bogus mailbox that's never really executed, and just allows you to investigate its content using the methods above.

What say you Lifters? Any thoughts around this from an API perspective? Are there better solutions that I'm missing?

Let me know your thoughts!

Cheers,

Matt

David Barri

unread,
Jun 28, 2013, 8:06:57 PM6/28/13
to lif...@googlegroups.com
Nice! I think this would be quite useful.

I've been doing similar things lately and I've found that the greater challenge is, once you have a mock class, how do you get it in place? If it's a variable in a snippet then it's a no-brainer but how do we get it to work with the numerous Lift's singleton objects? For instance, Mailer.sendMail creates a sends an internal actor a message and if we had a cool mock actor like in your example, it would only be half the puzzle. We'd need a way of putting it in place and then there are other concerns like restoration on teardown, and parallelism.

Sorry, I know you were asking feedback about the API. I find what you've got is perfect for unit testing. For functional tests, in addition to storing messages, I'd find it useful if I could configure it to send messages synchronously. That allows you to test interaction between a few components without a) having to setup a full integration environment, and b) without having to resort to Thread.sleep and eventually{} tricks to guess when async processing is done.

Matt Farmer

unread,
Jun 29, 2013, 11:28:49 AM6/29/13
to lif...@googlegroups.com
You can currently send messages synchronously using the !! call, can't you? 

Antonio Salazar Cardozo

unread,
Jun 29, 2013, 12:40:54 PM6/29/13
to lif...@googlegroups.com
On Saturday, June 29, 2013 11:28:49 AM UTC-4, Matt Farmer wrote:
You can currently send messages synchronously using the !! call, can't you? 

Only if the actor in question sends an explicit reply. Otherwise you wait for a reply forever, I believe.
Thanks,
Antonio

Matt Farmer

unread,
Jun 29, 2013, 12:46:08 PM6/29/13
to lif...@googlegroups.com
Bummer. Heh.

Ok, I've opened a pull request with this implemented if anyone is interested in having a look: https://github.com/lift/framework/pull/1467

Diego Medina

unread,
Jun 30, 2013, 11:57:39 PM6/30/13
to Lift

Hi Matt,

Does the containsMessage find case clssses with different values.

MyCaseClass (1) vs MyCaseClass (2)

?

Sent from my tablet
Diego

--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
 
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Diego Medina

unread,
Jul 1, 2013, 12:44:30 AM7/1/13
to Lift
and considering this is a mock, maybe giving access to the mailbox itself would be a good idea
--
Diego Medina
Lift/Scala Developer
di...@fmpwizard.com
http://fmpwizard.telegr.am

Matt Farmer

unread,
Jul 1, 2013, 8:19:16 AM7/1/13
to lif...@googlegroups.com
Yes. It's a List.contains on the bogus mailbox I implemented. It will distinguish between case class instances with different values. And I'm not so fond of the idea of giving raw access to the mailbox because I want to discourage people from checking the ordering of messages. Messages shouldn't really be order-dependent with actors. Checking the number of messages received and what messages they were should be enough.

You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/V7ixZLhc2TI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.

David Barri

unread,
Jul 1, 2013, 8:40:25 AM7/1/13
to lif...@googlegroups.com
A problem with hasReceivedMessage_? is that we're required to know the entire message contents in order to test. If a message was something like
  case class BroadcastNewUserRecord(id: Long, username: String)
a test case likely wouldn't care about the id (which probably wouldn't be deterministic); they'd likely only know the message type and username. This scenario wouldn't be testable without adding another method to the mock.

Another situation is that there's no way to check that a message was sent more than once. It's not a common requirement but that doesn't mean it doesn't happen.

I think that the received message collection should be exposed (at least a getter). Wanting to discourage order-checking is a noble goal but no rule can be applied to all situations all the time and there are going to be corner cases where a tester will need to check order. I think exposing the list would give testers the most flexibility so that they aren't restricted in unorthodox situations.

Cheers.

David Barri

unread,
Jul 1, 2013, 9:48:32 AM7/1/13
to lif...@googlegroups.com
On Sunday, 30 June 2013 01:28:49 UTC+10, Matt Farmer wrote:
You can currently send messages synchronously using the !! call, can't you? 

Yeah. What I mean though is I've got some tests where I need the actors to pass on msgs, and the code being tested uses ! because at runtime I want it to be async. It's only when I'm testing (and only in some tests I guess) that I want those async sends to be sync so that I don't start asserting before the msgs have been processed, and I don't want to use only !! for the sake of testability. What I've done is to override ! and route it to !! in those cases.

In retrospect I think it's probably beyond the scope of a general MockLiftActor. Just me thinking of every edge case in history as usual. Ignore this point of mine. :)

Matt Farmer

unread,
Jul 1, 2013, 10:07:13 AM7/1/13
to lif...@googlegroups.com
The id problem, I feel, could be sorted by preparing the test data properly. That said, the "received message multiple times" may be a valid use case for exposing the mailbox. I'll let it marinate for a bit and get back to you.

Thanks for the feedback! :)

--
Matt Farmer

Matt Farmer

unread,
Jul 1, 2013, 7:49:45 PM7/1/13
to lif...@googlegroups.com
Ok, after some further thinking about it, I decided to go ahead and expose the entire mailbox through the "messages" function. That should enable whatever kind of edge case beyond "has the mock actor received this message?" and "how many messages has the mock actor received?"

Let me know if y'all have any additional thoughts. :)

Diego Medina

unread,
Jul 1, 2013, 8:09:13 PM7/1/13
to Lift
nice :)


On Mon, Jul 1, 2013 at 7:49 PM, Matt Farmer <ma...@frmr.me> wrote:
Ok, after some further thinking about it, I decided to go ahead and expose the entire mailbox through the "messages" function. That should enable whatever kind of edge case beyond "has the mock actor received this message?" and "how many messages has the mock actor received?"

Let me know if y'all have any additional thoughts. :)

--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
 
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.
 
 

Antonio Salazar Cardozo

unread,
Jul 2, 2013, 4:24:27 PM7/2/13
to lif...@googlegroups.com
A proper mocking of LiftActor, btw, would merely check ! was called, rather than exposing anything at all about the mailbox.

This is what annoys me about unit tests: instead of just your original code being tightly coupled to the other objects it talks to, now your unit tests/mocks *and* your actual code are tightly coupled, so you double the cost of changing your interfaces. All problems, no solutions here, though, so I won't get into that :)
Thanks,
Antonio

Matt Farmer

unread,
Jul 2, 2013, 5:54:53 PM7/2/13
to lif...@googlegroups.com
Hm. Interesting thoughts. Do you think that the mock I wrote is a step in the right direction, or no?

Antonio Salazar Cardozo

unread,
Jul 3, 2013, 3:10:39 PM7/3/13
to lif...@googlegroups.com
I think it encourages more understanding of the internals of the actor than needed. I'd prefer something that did proper mocking if we're talking about true unit tests, i.e., it just let you check that ! was called with argument X, with possible ordering.
Thanks,
Antonio

Diego Medina

unread,
Jul 3, 2013, 3:38:05 PM7/3/13
to Lift
can't you do that with libraries like mockito, etc?

Antonio Salazar Cardozo

unread,
Jul 3, 2013, 6:21:26 PM7/3/13
to lif...@googlegroups.com
That is indeed what libraries like that try to provide. I can't remember off the top of my head how straightforward they are to use for actors. I remember running into some Scala related issues when I played with mockito and such. Doesn't change the fundamental issue with mocking, but if available, I think that's the way to properly unit test.
Thanks,
Antonio

Joe Barnes

unread,
Sep 3, 2014, 5:54:40 PM9/3/14
to lif...@googlegroups.com
So I've stumbled across these mock actors today.  I'm working on the Ubiquitous Chat App, lift-ng style, and I wanted to show we can test the ChatServer.  Unfortunately this doesn't really work as David Barri pointed out in this thread, because of the async nature of the actors against the synchronous nature of the tests.

The mock actor gets the create message from the ChatServer, but any updates aren't received.  I don't see them even if I do something ugly like a Thread.sleep.  I'm curious what everyone else is doing to get these mocks to work in testing.  If needed, I can throw this up onto github.

Joe

Joe Barnes

unread,
Sep 4, 2014, 12:09:39 AM9/4/14
to lif...@googlegroups.com
Well never mind.  I just needed to crack open a third beer to get my thinking right.  It turns out that I was just anticipating the list of messages to be in oldest-to-newest order, when it was the reverse.  I'm all set now!  Unless plans fall through, tomorrow a buddy of mine and I will be recording a 15-minute Ubiquitous Chat app with Lift => lift-ng => Angular

Joe
Reply all
Reply to author
Forward
0 new messages