Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pykka test support #25

Closed
fatuhoku opened this issue Sep 1, 2013 · 10 comments
Closed

Pykka test support #25

fatuhoku opened this issue Sep 1, 2013 · 10 comments
Assignees
Labels
Milestone

Comments

@fatuhoku
Copy link

fatuhoku commented Sep 1, 2013

One of the main advantages of using the Actor model is its ease of testing.

Every actor is by default isolated and so verifying the individual behaviours of Actors should be easy because we can provide each actor with fake / stub collaborators and verify that the right messages (of the appropriate formats) are sent and received.

However, it's not simple to capture this in the form of an assertion in a unit test.

Perhaps a couple of examples of how Pykka actors can be tested with popular test/mocking frameworks would help here.

@jstasiak
Copy link
Contributor

jstasiak commented Sep 2, 2013

I don't see much difference between actors and "regular" classes here. You

  1. Start an actor with defined collaborators/dependencies/etc.
  2. Ask actor about something (ask instead of tell so actor finishes processing the message before test resumes it's execution)
  3. Make assertions using collaborators state, value(s) returned by the actor or both
  4. Stop the actor to not pollute actor system for other tests

@fatuhoku
Copy link
Author

fatuhoku commented Sep 2, 2013

The flaw there is of course in 2., where an assumption has been made about the actor's behaviour wrt. to returning a message by blocking.

What if I purposefully want to test a simple actor system like the one below:

ProducerActor -------------------------------> ConsumerActor
                        {'produced': 'apple'}

Let's assume that ProducerActor is written in such a way that it emits the dictionary {'produced': 'apple'} as soon as it's been created. The reference to ConsumerActor is correctly set up.

The test then, is simply to expect that ConsumerActor eventually receives the produced apple. It's not obvious to me how this is achievable with any testing library out of the box.

@fatuhoku
Copy link
Author

fatuhoku commented Sep 2, 2013

While this seems like a feature to ask of a testing library, it's at least Pykka's responsibility to clarify its position wrt. async/synchronous testing. Akka, for example shows you how you could test actor behaviour synchronously on one thread so that tests are well-behaved.

@fatuhoku
Copy link
Author

The most straightforward way I have found of verifying message-passing behaviour is to create a dummy test Actor (I call this NopActor, see below. I think such a stub class should be provided by Pykka).

Set up your actor system, interact with the NopActor and then basically just inspect the mailbox for the message.

class NopActor(pykka.ThreadingActor):
    """
    Does nothing. For testing purposes only.
    """

    def __init__(self):
        super(NopActor, self).__init__()


def test_should_be_able_to_assert_actor_message_passing_interactions():
    nop_actor = NopActor.start()
    sent_message = {'key': 'value'}
    nop_actor.tell(sent_message)
    inbox = copy.copy(nop_actor.actor_inbox)
    received = inbox.get(block=False)
    received.should.be.equal(sent_message)

The copy.copy() calls was used to be take a snapshot of the actor_inbox so that when we inspect the queue with get(), we don't really remove any messages that would affect the running state of the actor system under test (in our case, just a single instance of NopActor pending processing of the {'key':'value'} message).

@fatuhoku
Copy link
Author

If you like this style of testing (well, it works...!) please consider documenting this on the README to make Pykka more compatible to TDD / BDD.

@jstasiak
Copy link
Contributor

It seems to me that you're trying to test the wrong thing. Pykka (actors, message passing etc.) is already tested.

If I wanted to test that your ProducerActor sends a message then I'd do something like this:

from mock import Mock
from pykka import ThreadingActor

class ProducerActor(ThreadingActor):
    def __init__(self, consumer):
        self.consumer = consumer
    # the code that interacts with consumer goes somewhere below


def test_that_producer_sends_something_after_its_started():
    consumer = Mock()

    producer = ProducerActor.start(consumer)
    producer.stop()

    consumer.tell.assert_called_once_with({'produced': 'apple'})

@fatuhoku
Copy link
Author

Okay, thanks @jstasiak; though due to #26, to avoid the tests from hanging indefinitely you have to

consumer = Mock()

What you really have to do is to apply #26's workaround:

from pykka import ActorRef
consumer = MagicMock(spec=ActorRef)

@fatuhoku
Copy link
Author

I'm now wondering how it's possible to run a 'wiring test'. A system of actors all need to be alive and running when the application is running in order for the application to work fully!

@jodal
Copy link
Owner

jodal commented Sep 17, 2013

I haven't done such a test myself, but I'd be interested in hearing about it you find a convenient way to test this.

@jodal jodal closed this as completed in 33c1b05 May 3, 2019
@jodal jodal added this to the v2.0 milestone May 3, 2019
@jodal jodal self-assigned this May 3, 2019
@jodal
Copy link
Owner

jodal commented May 3, 2019

I've created a testing section in the docs, initially with the tips from this issue and some pytest examples: https://www.pykka.org/en/develop/testing/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants