10. Testing, building and running

A working agent should establish its promised result and expose failures clearly. A useful test checks that contract: requesting a valid light state produces the matching observation, an invalid state fails, and a conversation reports an outcome within a bounded time.

Run the guide examples

The sample project contains the complete programs from these chapters, the Java device and a Maven build for ASTRA 2.5.2. Extract it and run from its project directory:

mvn clean compile astra:run -Dastra.main=LightModel
mvn compile astra:run -Dastra.main=LightController
mvn compile astra:run -Dastra.main=DeviceController
mvn compile astra:run -Dastra.main=ReliableController
mvn compile astra:run -Dastra.main=ActivityCounter
mvn compile astra:run -Dastra.main=LightClient
mvn compile astra:run -Dastra.main=EventController
mvn test

Each command starts a fresh application and exits after reporting its result. The project does not require an environment JAR, hardware or an external messaging service. The guide uses the local message transport supplied by ASTRA.

Program

Evidence to look for

LightModel

light = on

LightController

light follows switch: on

DeviceController

observed light = on

ReliableController

Invalid target rejected, followed by a valid target achieved

ActivityCounter

target reached

LightClient

room confirmed light = on

EventController

event confirmed light = on

Intermediate event messages may interleave. Judge the final state and conversation outcome rather than relying on the ordering of unrelated prints.

Test observable behavior

The astra-unittest library supports test agents inheriting astra.unit.ASTRAUnitTest and the behavior under test. A test is a test_... goal; assertions receive its TestSuite parameter. Keep application code and tests in named packages when using inheritance, since generated Java cannot import classes from the default package.

The sample project includes a test agent under src/test/astra/tests/LightDeviceTests.astra:

package tests;

import astra.unit.*;

agent LightDeviceTests extends ASTRAUnitTest {
    module example.LightDevice device;
    types lighting { formula light(string); }

    plan +!test_invalid_target(TestSuite suite) {
        wait(light("off"), 2000);
        boolean Rejected = false;
        try {
            device.set("dim");
        } recover {
            Rejected = true;
        }
        UT.assertTrue(suite, Rejected);
        UT.assertEquals(suite, "off", device.state());
    }

    plan +!test_valid_target(TestSuite suite) {
        device.set("on");
        wait(light("on"), 2000);
        UT.assertEquals(suite, "on", device.state());
        device.set("off");
        wait(light("off"), 2000);
    }
}

Run mvn test. The invalid-target test confirms both rejection and unchanged device state. The valid-target test confirms the observation and restores the device afterward. The example uses astra-unittest:2.5.2 as a test dependency.

For the light controller, also cover:

  • A target already observed: the goal completes without an unnecessary change.

  • A valid new target: the action occurs and the corresponding observation follows.

  • An invalid target: failure reaches the caller rather than being reported as success.

  • A missing or delayed observation: the wait times out and recovery runs.

  • A request conversation: acceptance is followed by a matching completion or failure response.

Use a controllable fake device for delayed and failed operations. Tests should establish their own starting state and use bounded waits for asynchronous effects. A fixed sleep can hide a race on a fast machine and fail on a slower one.

The detailed testing page includes assertions and multi-agent tests. Select a testing-library release compatible with your project’s ASTRA version; do not infer compatibility merely from a similar artifact name.

Understand the build boundary

Java modules live in src/main/java; ASTRA programs live in src/main/astra. The ASTRA Maven plugin compiles agent programs to Java and makes the generated classes part of the application. Test sources belong under src/test/astra.

The astra-base parent supplies core dependencies and plugin configuration. Keep the parent and compiler plugin versions aligned. Add adapters and application dependencies deliberately; custom Java modules normally need no additional dependency beyond the core project.

astra.main chooses the initial agent class. astra.name chooses that initial instance’s name. Agents created later still need unique instance names and may need an explicit startup goal.

Finish the application lifecycle

The sample agents use system.exit() because their demonstrations are finite. A service should remain alive to handle requests and observations, terminate individual agents when appropriate and manage external resources explicitly.

When packaging an application, preserve the dependencies and configuration it needs at runtime. Test the packaged form from its actual working directory: relative environment paths, configuration files and Java class loading can differ from a development run.

Continue with the Examples, connect an Environment, or consult the API Reference Guide.

Further Details