Acceptance tests for Embabel Agent Framework using Latest Snapshot Build, A2A JSON-RPC Endpoint, Testcontainers and JUnit 5.
This test suite automatically:
- Downloads
example-agent-javafrom Embabel Artifactory using Apache Maven - Builds and launches it as a Docker container
- Runs focused acceptance tests to verify agent functionality via A2A protocol.
Architecture: All tests extend AbstractA2ATest for minimal, DDD-focused testing.
# 1. Ensure Docker is running
docker --version
# 2. Run all tests
./mvnw clean test
# 3. Run specific test
./mvnw test -Dtest=HoroscopeAgentTestNo credentials needed! Maven wrapper and Artifactory access is automatic.
- Java 21 or later
- Docker installed and running
- Maven wrapper (included in project)
All agent tests follow a minimal, single-responsibility pattern:
AbstractA2ATest (abstract)
↓
├── HoroscopeAgentTest
├── FactCheckerAgentTest
├── AdventureAgentTest
└── ... (9 total agent tests)
Each test focuses on one question: Does this agent do what it's supposed to do?
Abstract base class providing:
- ✅ Payload loading and caching
- ✅ HTTP request handling
- ✅ Domain-driven assertions
- ✅ Common infrastructure
Subclasses only implement:
protected abstract String getPayloadPath(); // e.g., "payloads/horoscope-agent-request.json"
protected abstract String getRequestId(); // e.g., "req-001"@ExtendWith(EmbabelA2AServerExtension.class)
@DisplayName("Horoscope Agent Tests")
class HoroscopeAgentTest extends AbstractA2ATest {
@Override
protected String getPayloadPath() {
return "payloads/horoscope-agent-request.json";
}
@Override
protected String getRequestId() {
return "req-001";
}
@Test
@DisplayName("Should send horoscope message and receive AI-generated story")
void shouldSendHoroscopeMessageAndReceiveStory(ServerInfo server) {
log("Sending horoscope message to server at: " + server.getBaseUrl());
Response response = sendA2ARequest(server, payload);
assertSuccessfulA2AResponse(response);
if (response.getStatusCode() == 200) {
assertJsonRpcCompliance(response);
assertContentPresent(response);
log("✓ Horoscope message sent and response received");
} else {
log("✓ Message accepted for async processing");
}
}
}// Domain-driven assertions
protected void assertSuccessfulA2AResponse(Response response)
protected void assertJsonRpcCompliance(Response response)
protected void assertContentPresent(Response response)
// Infrastructure methods
protected Response sendA2ARequest(ServerInfo server, String payload)
protected String loadJsonPayload(String resourcePath)
protected void log(String message)@Test
void myTest(ServerInfo server) {
String baseUrl = server.getBaseUrl(); // Full URL
int port = server.getPort(); // Mapped port
GenericContainer<?> container = server.getContainer(); // Testcontainers instance
}All extend AbstractA2ATest:
| Test Class | Agent Function | Status |
|---|---|---|
HoroscopeAgentTest |
Generate horoscope stories | ✅ Active |
FactCheckerAgentTest |
Fact-check content | ✅ Active |
AdventureAgentTest |
Create adventure stories | ⏸️ Disabled |
BankSupportAgentTest |
Handle banking queries | ⏸️ Disabled |
BookWriterAgentTest |
Write books | ⏸️ Disabled |
InterestingFactsAgentTest |
Find interesting facts | ⏸️ Disabled |
MealPreparationAgentTest |
Prepare meals | ⏸️ Disabled |
StoryRevisionAgentTest |
Revise stories | ⏸️ Disabled |
WikiAgentTest |
Research on Wikipedia | ⏸️ Disabled |
EmbabelA2AServerBasicTest - Verifies server startup
- Server starts successfully
- Health endpoint responds
- Server info accessible
Test Execution
↓
@ExtendWith(EmbabelA2AServerExtension.class)
↓
Maven Wrapper Downloads JAR
├─ From: https://repo.embabel.com/artifactory/libs-release
└─ Or: https://repo.embabel.com/artifactory/libs-snapshot
↓
Builds Docker Image (eclipse-temurin:21-jre-alpine)
↓
Starts Container (8080 → Random Host Port)
↓
Tests Execute Against Running Server
↓
Container Stops on JVM Shutdown
Default configuration (in extension class):
private static final String DEFAULT_VERSION = "0.3.3-SNAPSHOT";
private static final int DEFAULT_SERVER_PORT = 8080;
private static final String DEFAULT_JVM_ARGS = "-Xmx512m";
private static final Duration DEFAULT_STARTUP_TIMEOUT = Duration.ofMinutes(2);To modify: Edit EmbabelA2AServerExtension.java
The extension uses a singleton pattern:
- Container initialized once per test run
- Shared across all test classes
- Starts before first test
- Stops on JVM shutdown
Benefits:
- ⚡ Fast test execution (no repeated startup)
- 💰 Resource efficient
- 🔄 Consistent state
# Run all tests
./mvnw test
# Run specific test
./mvnw test -Dtest=HoroscopeAgentTest
# Run only active tests
./mvnw test -Dtest=HoroscopeAgentTest,FactCheckerAgentTest
# Run with verbose output
./mvnw test -X
# Clean and test
./mvnw clean test@ExtendWith(EmbabelA2AServerExtension.class)
@DisplayName("My New Agent Tests")
class MyNewAgentTest extends AbstractA2ATest {
@Override
protected String getPayloadPath() {
return "payloads/my-agent-request.json";
}
@Override
protected String getRequestId() {
return "req-010";
}
@Test
@DisplayName("Should perform agent-specific action")
void shouldPerformAction(ServerInfo server) {
log("Testing new agent at: " + server.getBaseUrl());
Response response = sendA2ARequest(server, payload);
assertSuccessfulA2AResponse(response);
if (response.getStatusCode() == 200) {
assertJsonRpcCompliance(response);
assertContentPresent(response);
log("✓ Action completed successfully");
}
}
}That's all you need! ~30-40 lines per test.
agent-acceptance-tests/
├── src/
│ ├── main/java/
│ │ └── (none - test-only project)
│ └── test/
│ ├── java/com/embabel/acceptance/
│ │ ├── agent/
│ │ │ ├── AbstractA2ATest.java ← Base class
│ │ │ ├── HoroscopeAgentTest.java ← Agent tests
│ │ │ ├── FactCheckerAgentTest.java
│ │ │ └── ... (7 more agent tests)
│ │ ├── basic/
│ │ │ └── EmbabelA2AServerBasicTest.java ← Infrastructure test
│ │ └── jupiter/
│ │ └── EmbabelA2AServerExtension.java ← JUnit extension
│ └── resources/
│ └── payloads/
│ ├── horoscope-agent-request.json
│ ├── fact-checker-request.json
│ └── ... (7 more payloads)
├── pom.xml
├── mvnw ← Maven wrapper
├── mvnw.cmd
└── README.md
Cannot connect to Docker daemon
Solution: Start Docker Desktop
Maven wrapper not found
Solution: The extension searches parent directories. Ensure mvnw exists in project root or parent.
Container did not start within 2 minutes
Solutions:
- Check Docker has adequate resources (memory, CPU)
- Increase timeout in
EmbabelA2AServerExtension.java - Check container logs for errors
This is normal! First run:
- Downloads Maven dependencies (~100MB)
- Downloads example-agent-java artifact
- Builds Docker image
Subsequent runs are much faster (everything cached).
JAR not found at expected path
Solutions:
- Verify version
0.3.3-SNAPSHOTexists in Embabel Artifactory - Check network access to
repo.embabel.com - Clear Maven local repository:
rm -rf ~/.m2/repository/com/embabel
✅ DDD-Focused - Domain-driven design with clear concepts
✅ Minimal Tests - ~50 lines per agent test
✅ Single Responsibility - Each test answers one question
✅ Zero Duplication - Common code in base class
✅ Automatic Download - Maven wrapper handles dependencies
✅ Singleton Container - Fast, resource-efficient
✅ Docker Isolation - Clean test environment
✅ Random Ports - No port conflicts
✅ CI/CD Ready - Works in all environments
name: Acceptance Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'
- name: Run tests
run: ./mvnw clean testacceptance-tests:
image: maven:3.9-eclipse-temurin-21
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2375
script:
- ./mvnw clean testReady to test?
./mvnw clean testThat's it! 🚀
