Skip to content

Commit 9605717

Browse files
port of behaviour testing
1 parent 6958ffd commit 9605717

File tree

11 files changed

+1084
-0
lines changed

11 files changed

+1084
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Tutorial 20.0: Basic Sync Agent Testing
2+
3+
Learn how to write automated tests for sync agents using the AgentEx testing framework.
4+
5+
## What You'll Build
6+
7+
Automated tests for sync agents that verify:
8+
- Basic response capability
9+
- Multi-turn conversation
10+
- Context maintenance
11+
- Response content validation
12+
13+
## Prerequisites
14+
15+
- AgentEx services running (`make dev`)
16+
- A sync agent running (Tutorial 00_sync/000_hello_acp recommended)
17+
18+
## Quick Start
19+
20+
Run the tests:
21+
```bash
22+
pytest test_sync_agent.py -v
23+
```
24+
25+
## Understanding Sync Agent Testing
26+
27+
Sync agents respond **immediately** via the `send_message()` API. Testing them is straightforward:
28+
29+
```python
30+
from agentex.lib.testing import test_sync_agent
31+
32+
def test_basic_response():
33+
with test_sync_agent() as test:
34+
response = test.send_message("Hello!")
35+
assert response is not None
36+
```
37+
38+
## The Test Helper: `test_sync_agent()`
39+
40+
The `test_sync_agent()` context manager:
41+
1. Connects to AgentEx
42+
2. Finds a sync agent
43+
3. Creates a test task
44+
4. Returns a `SyncAgentTest` helper
45+
5. Automatically cleans up the task when done
46+
47+
## Key Methods
48+
49+
### `send_message(content: str) -> TextContent`
50+
Send a message and get immediate response (no async/await).
51+
52+
### `get_conversation_history() -> list[TextContent]`
53+
Get all messages exchanged in the test session.
54+
55+
## Common Assertions
56+
57+
```python
58+
from agentex.lib.testing import (
59+
assert_valid_agent_response,
60+
assert_agent_response_contains,
61+
assert_conversation_maintains_context,
62+
)
63+
64+
# Response is valid
65+
assert_valid_agent_response(response)
66+
67+
# Response contains specific text
68+
assert_agent_response_contains(response, "hello")
69+
70+
# Agent maintains context
71+
test.send_message("My name is Alice")
72+
test.send_message("What's my name?")
73+
history = test.get_conversation_history()
74+
assert_conversation_maintains_context(history, ["Alice"])
75+
```
76+
77+
## Test Pattern
78+
79+
A typical sync agent test follows this pattern:
80+
81+
1. **Setup** - `with test_sync_agent() as test:`
82+
2. **Action** - `response = test.send_message("...")`
83+
3. **Assert** - Validate response
84+
4. **Cleanup** - Automatic when context manager exits
85+
86+
## Tips
87+
88+
- Tests skip gracefully if AgentEx isn't running
89+
- Each test gets a fresh task (isolated)
90+
- Conversation history tracks all exchanges
91+
- Use descriptive test names that explain what behavior you're testing
92+
93+
## Next Steps
94+
95+
- Complete Tutorial 20.1 for agentic agent testing
96+
- Apply these patterns to test your own agents
97+
- Integrate tests into your development workflow
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""
2+
Tutorial 20.0: Basic Sync Agent Testing
3+
4+
This tutorial demonstrates how to test sync agents using the agentex.lib.testing framework.
5+
6+
Prerequisites:
7+
- AgentEx services running (make dev)
8+
- A sync agent running (e.g., tutorial 00_sync/000_hello_acp)
9+
10+
Run:
11+
pytest test_sync_agent.py -v
12+
"""
13+
14+
from agentex.lib.testing import (
15+
assert_agent_response_contains,
16+
assert_conversation_maintains_context,
17+
assert_valid_agent_response,
18+
test_sync_agent,
19+
)
20+
21+
22+
def test_sync_agent_responds():
23+
"""Test that sync agent responds to a simple message."""
24+
with test_sync_agent() as test:
25+
# Send a message
26+
response = test.send_message("Hello! How are you?")
27+
28+
# Verify we got a valid response
29+
assert_valid_agent_response(response)
30+
print(f"✓ Agent responded: {response.content[:50]}...")
31+
32+
33+
def test_sync_agent_multi_turn():
34+
"""Test that sync agent handles multi-turn conversation."""
35+
with test_sync_agent() as test:
36+
# First exchange
37+
response1 = test.send_message("Hello!")
38+
assert_valid_agent_response(response1)
39+
40+
# Second exchange
41+
response2 = test.send_message("Can you help me with something?")
42+
assert_valid_agent_response(response2)
43+
44+
# Third exchange
45+
response3 = test.send_message("Thank you!")
46+
assert_valid_agent_response(response3)
47+
48+
# Verify conversation history
49+
history = test.get_conversation_history()
50+
assert len(history) >= 6 # 3 user + 3 agent messages
51+
print(f"✓ Completed {len(history)} message conversation")
52+
53+
54+
def test_sync_agent_context():
55+
"""Test that sync agent maintains conversation context."""
56+
with test_sync_agent() as test:
57+
# Establish context
58+
response1 = test.send_message("My name is Sarah and I'm a teacher")
59+
assert_valid_agent_response(response1)
60+
61+
# Query the context
62+
response2 = test.send_message("What is my name?")
63+
assert_valid_agent_response(response2)
64+
65+
# Check context is maintained (agent should mention Sarah)
66+
history = test.get_conversation_history()
67+
assert_conversation_maintains_context(history, ["Sarah"])
68+
print("✓ Agent maintained conversation context")
69+
70+
71+
def test_sync_agent_specific_content():
72+
"""Test that agent responds with expected content."""
73+
with test_sync_agent() as test:
74+
# Ask a factual question
75+
response = test.send_message("What is 2 plus 2?")
76+
77+
# Verify response is valid
78+
assert_valid_agent_response(response)
79+
80+
# Verify response contains expected content
81+
# (This assumes the agent can do basic math)
82+
assert_agent_response_contains(response, "4")
83+
print(f"✓ Agent provided correct answer: {response.content[:50]}...")
84+
85+
86+
def test_sync_agent_conversation_length():
87+
"""Test conversation history tracking."""
88+
with test_sync_agent() as test:
89+
# Send 3 messages
90+
test.send_message("First message")
91+
test.send_message("Second message")
92+
test.send_message("Third message")
93+
94+
# Get history
95+
history = test.get_conversation_history()
96+
97+
# Should have 6 messages: 3 user + 3 agent
98+
assert len(history) >= 6, f"Expected >= 6 messages, got {len(history)}"
99+
print(f"✓ Conversation history contains {len(history)} messages")
100+
101+
102+
if __name__ == "__main__":
103+
print("Run with: pytest test_sync_agent.py -v")
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Tutorial 20.1: Agentic Agent Testing
2+
3+
Learn how to test agentic agents that use event-driven architecture and require polling.
4+
5+
## What You'll Learn
6+
7+
- How agentic agent testing differs from sync testing
8+
- Using async context managers for testing
9+
- Configuring timeouts for polling
10+
- Testing event-driven behavior
11+
12+
## Prerequisites
13+
14+
- AgentEx services running (`make dev`)
15+
- An agentic agent running (Tutorial 10_agentic recommended)
16+
- Understanding of async/await in Python
17+
18+
## Quick Start
19+
20+
Run the tests:
21+
```bash
22+
pytest test_agentic_agent.py -v
23+
```
24+
25+
## Key Differences from Sync Testing
26+
27+
| Aspect | Sync Testing | Agentic Testing |
28+
|--------|-------------|-----------------|
29+
| Response | Immediate | Requires polling |
30+
| Method | `send_message()` | `send_event()` |
31+
| Context manager | Sync (`with`) | Async (`async with`) |
32+
| Test function | Regular function | `@pytest.mark.asyncio` |
33+
| Timeout | N/A | Configure per request |
34+
35+
## The Agentic Test Helper
36+
37+
```python
38+
import pytest
39+
from agentex.lib.testing import test_agentic_agent
40+
41+
@pytest.mark.asyncio
42+
async def test_my_agent():
43+
async with test_agentic_agent() as test:
44+
# Send event and wait for response
45+
response = await test.send_event("Hello!", timeout_seconds=15.0)
46+
assert response is not None
47+
```
48+
49+
## Understanding Timeouts
50+
51+
Agentic agents process events asynchronously, so you need to:
52+
1. Send the event
53+
2. Poll for the response
54+
3. Wait up to `timeout_seconds`
55+
56+
**Default timeout**: 15 seconds
57+
**Recommended timeout**: 20-30 seconds for complex operations
58+
59+
If the agent doesn't respond within the timeout, you'll get a `RuntimeError` with diagnostic information.
60+
61+
## Testing Patterns
62+
63+
### Basic Response
64+
```python
65+
@pytest.mark.asyncio
66+
async def test_agentic_responds():
67+
async with test_agentic_agent() as test:
68+
response = await test.send_event("Hello!", timeout_seconds=15.0)
69+
assert_valid_agent_response(response)
70+
```
71+
72+
### Multi-Turn Conversation
73+
```python
74+
@pytest.mark.asyncio
75+
async def test_conversation():
76+
async with test_agentic_agent() as test:
77+
r1 = await test.send_event("My name is Alex", timeout_seconds=15.0)
78+
r2 = await test.send_event("What's my name?", timeout_seconds=15.0)
79+
80+
history = await test.get_conversation_history()
81+
assert len(history) >= 2
82+
```
83+
84+
### Long-Running Operations
85+
```python
86+
@pytest.mark.asyncio
87+
async def test_complex_task():
88+
async with test_agentic_agent() as test:
89+
# Some agents need more time for complex work
90+
response = await test.send_event(
91+
"Analyze this data...",
92+
timeout_seconds=30.0 # Longer timeout
93+
)
94+
assert response is not None
95+
```
96+
97+
## Troubleshooting
98+
99+
**TimeoutError**: Agent didn't respond in time
100+
- Increase `timeout_seconds`
101+
- Check agent is running
102+
- Check AgentEx logs for errors
103+
104+
**No agentic agents available**:
105+
- Run an agentic tutorial agent first
106+
- Check `await client.agents.list()` shows agentic agents
107+
108+
## Next Steps
109+
110+
- Test your own agentic agents
111+
- Explore temporal agent testing for workflow-based agents
112+
- Integrate behavior tests into CI/CD

0 commit comments

Comments
 (0)