Skip to content

jade-oo: Making Agents testable

April 1, 2010

The core of most JADE-based systems are the Behaviours that respond to incoming messages, sent to them by other Agents (and which, in turn, send messages to other Behaviours). Testing these Behaviours is critical, but complicated; instantiating a standard JADE Behaviour in a test involves instantiating much of the JADE architecture that, through inheritance, is required for the existence of any Behaviour; and that underlying architecture provides mechanisms used by Behaviours that aren’t accessible to the tests.

jade-oo decouples behavioural logic from the underlying JADE system, allowing isolated testing of these critical components. Here’s how…

The current jade-oo example consists of two Agents sending messages to each other. One Agent, TimeRequestorAgent, wants to know what the time is, and the other Agent, TimeResponderAgent, responds with the current time.

Each Agent has one Behaviour. The TimeRequestorAgent has a jade-oo CyclicBehaviour that, every 12 seconds, sends a message requesting the time. The TimeResponderAgent has one jade-oo MessageReceiver Behaviour, which, every time a message is received, replies with the current time.

We want to test the execution of the business logic contained within the TimeResponderAgent. That logic isn’t within TimeResponderAgent itself, nor in the MessageReceiver Behaviour; it’s in the TellTheTime POJO that MessageReciver offloads its messages onto. Here’s the TellTheTime class in full:

package com.dantwining.jade.oo;

import jade.core.AID;
import jade.oo.core.behaviours.MessageReceiver;
import jade.oo.core.behaviours.Outbox;

public class TellTheTime implements MessageReceiver {

        private final Outbox outbox;

        public TellTheTime(Outbox outbox) {
                this.outbox = outbox;
        }

        @Override
        public void receive(String message, AID sender) {
                long currentTime = System.currentTimeMillis();
                outbox.send(currentTime, sender);
        }
}

In the real world, this class (and the classes that it in turn uses) are likely to be much more complicated. These are the classes that you want to test; the classes that add your functionality on top of the JADE framework.

Here’s the test for TellTheTime:

package com.dantwining.jade.oo;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import jade.core.AID;

import org.junit.Test;

public class TellTheTimeTest {

        @Test
        public void shouldReturnCurrentTimeToRequestingAgent() {
                // Given standard construction...
                FakeOutbox fakeOutbox = new FakeOutbox();
                TellTheTime tellTheTime = new TellTheTime(fakeOutbox);
                AID requester = new AID("requester", AID.ISGUID);

                // ...when an incoming message is received...
                long timeBefore = System.currentTimeMillis();
                tellTheTime.receive(null, requester);
                long timeAfter = System.currentTimeMillis();

                // ...a reply should be sent to the requester, with an appropriate time.
                assertEquals(requester, fakeOutbox.getRecipient());

                long reportedTime = (Long) fakeOutbox.getMessage();
                assertTrue(timeBefore <= reportedTime);
                assertTrue(timeAfter >= reportedTime);
        }

}

Because MessageReceiver guarantees that your receive() method will only be called if a message is available, you don’t have to test for “message not present” behaviour. In fact, I can’t think of any other tests for this Behaviour, but please post a comment if you can.

Compare our TellTheTime code with a standard JADE Behaviour that performs the same task:

package com.dantwining.jade.oo;

import jade.core.behaviours.CyclicBehaviour;
import jade.lang.acl.ACLMessage;

import java.io.IOException;

public class TimeResponderJadeBehaviour extends CyclicBehaviour {

    @Override
    public void action() {
        ACLMessage message = myAgent.receive();

        if (message == null) {
            block();
            return;
        }

        long currentTime = System.currentTimeMillis();
        ACLMessage reply = message.createReply();
        try {
            reply.setContentObject(currentTime);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        myAgent.send(reply);
    }
}

How can we test this Behaviour? We’d probably want tests that:

  1. Ensure that no action is taken if no message is received.
  2. Ensure that any reply is addressed to the Agent who asked for the time.
  3. Ensure that the time sent is accurate/appropriate.

But how can we…

  1. Control myAgent.receive() so that it does/doesn’t return a test message?
  2. Capture the argument to myAgent.send() to that we can check its address?
  3. Capture the argument to myAgent.send() to that we can check its contents?

In standard JADE, receive(), send() and block() are all hidden side-effects. In jade-oo we (a) expose Outbox and Block so that interactions with them are testable, and (b) encapsulate responsibility in the MessageReceiver so that client code is only invoked when a message is available for processing, and doesn’t know (or care) about any other states.


Caveat: I have to admit that I’ve glossed over a few things here. In standard JADE, messaging is more complex than implied here. For example, messages can be filtered by template, conversation and so on. However, it’s quite straightforward to extend the jade-oo MessageReceiver to include all of these mechanisms. Also, the underlying JADE framework isn’t (yet) typesafe. The generics used by MessageReceiver imply that the object returned will be strongly typed. It isn’t; ClassCastExceptions may still occur, but this is the case with both JADE and jade-oo.

Advertisements
3 Comments leave one →
  1. April 3, 2010 1:07 pm

    I’m not sure I like a POJO being called “TellTheTime”, it sounds too much like a method. I would have gone with something more like “TimeTeller”/”TimeResponder”, thoughts?
    I don’t get why TimeResponderJadeBehaviour is a CyclicBehaviour but then I haven’t worked with JADE for long enough to have forgotten it all.

    You don’t explain what “Outbox” is. Is it a Jade-oo version of a Jade object I don’t know about or is it an abstraction of your own design? I would go and sniff through your source code but my lunch is ready…

    Matt

  2. April 3, 2010 6:51 pm

    Just re-read my comment after it popped back into my feed reader and noticed it contains a pretty ropey sentence:
    “…I haven’t worked with JADE for long enough to have forgotten it all.”
    By which I meant “I’ve forgotten it all” 😉

  3. April 3, 2010 9:39 pm

    GWB. Here’s everything I can think of right now:

    1) I’m not sure about TellTheTime either; TimeResponder is better (TimeTeller doesn’t convey the fact that the object returns the current time to the sender of the message (I say this not as criticism of your input, but as acknowledgment that good names are hard to find)).

    2) TimeResponderJadeBehaviour is a CyclicBehaviour because it has to go round and round and round and round, blocking (by which I mean, sleeping) between incoming messages. The only difference between a Behaviour and a CyclicBehaviour is that a CyclicBehaviour has its done() method hardcoded to return false, and so the Agent never removes it from its pool of active Behaviours.

    3) Outbox is an abstraction of my own design, and one that (along with Inbox) deserves an article all of its own. In short, rather than having an Agent be responsible for the receiving of messages, the sending of messages, and the scheduling of Behaviours, in jade-oo an Agent HAS-A Inbox (responsible for the receiving of messages), HAS-A Outbox (responsible for the sending of messages), and is itself only responsible for the scheduling of Behaviours. +1 for the SRP boys…

    4) If you think asking me all these questions means that I’ll spend my SCWCD revision weekend thinking about jade-oo rather than Servlets and JSP, then you’re wrong. But when I score less* than you, I’m blaming you anyway.

    (* or do I mean fewer? 😉 )

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: