Skip to content

Commit e48bbbc

Browse files
committed
fix: incorporate review comments
1 parent b7b0d76 commit e48bbbc

8 files changed

Lines changed: 375 additions & 101 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
flowchart TD
2+
A[Start Test] --> B[Create DockerContainer]
3+
B --> C{Image exists?}
4+
C -->|No| D[Pull image]
5+
C -->|Yes| E[Create container]
6+
D --> E
7+
E --> F[Start container]
8+
F --> G[Wait for ready signal]
9+
G --> H[Run test code]
10+
H --> I[Stop container]
11+
I --> J[Remove container]
12+
J --> K[Test complete]
13+
14+
style A fill:#e1f5fe
15+
style K fill:#c8e6c9
16+
style H fill:#fff9c4
36.7 KB
Loading

content/learning-paths/cross-platform/automate-mcp-with-testcontainers/github-actions-ci.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Configure GitHub Actions for CI/CD
3-
weight: 5
3+
weight: 6
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
sequenceDiagram
2+
participant Test as Pytest Test
3+
participant TC as Testcontainers
4+
participant MCP as MCP Server Container
5+
6+
Test->>TC: Create DockerContainer
7+
TC->>MCP: Start container
8+
TC-->>Test: Container ready
9+
10+
Test->>MCP: initialize request
11+
MCP-->>Test: capabilities response
12+
13+
Test->>MCP: initialized notification
14+
15+
Test->>MCP: tools/call (check_image)
16+
MCP-->>Test: tool result
17+
18+
Test->>MCP: tools/call (knowledge_base_search)
19+
MCP-->>Test: tool result
20+
21+
Test->>TC: Exit context manager
22+
TC->>MCP: Stop & remove container
54.4 KB
Loading
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
---
2+
title: Run a basic Testcontainers example
3+
weight: 4
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## Understand Testcontainers basics
10+
11+
Before writing full integration tests, explore how Testcontainers manages containers programmatically. This section walks you through basic examples that demonstrate the core concepts.
12+
13+
## Run a simple container
14+
15+
Create a Python script called `basic_example.py` to see how Testcontainers works:
16+
17+
```python
18+
from testcontainers.core.container import DockerContainer
19+
20+
# Start a container with a long-running process to keep it alive
21+
with DockerContainer("alpine:latest").with_command("sleep infinity") as container:
22+
# Execute a command inside the running container
23+
exit_code, output = container.exec("echo 'Hello from Testcontainers on Arm!'")
24+
print(output.decode("utf-8"))
25+
```
26+
27+
The `.with_command("sleep infinity")` keeps the container running so you can execute commands inside it. Without this, the container would exit immediately.
28+
29+
Run the script:
30+
31+
```bash
32+
python basic_example.py
33+
```
34+
35+
The output shows the message from inside the container:
36+
37+
```output
38+
Hello from Testcontainers on Arm!
39+
```
40+
41+
When the `with` block exits, Testcontainers automatically stops and removes the container.
42+
43+
## Verify container architecture
44+
45+
Since you're running on an Arm machine, verify the container uses the correct architecture:
46+
47+
```python
48+
from testcontainers.core.container import DockerContainer
49+
50+
with DockerContainer("python:3.11-slim").with_command("sleep infinity") as container:
51+
exit_code, output = container.exec("uname -m")
52+
arch = output.decode("utf-8").strip()
53+
print(f"Container architecture: {arch}")
54+
assert arch in ("aarch64", "arm64"), f"Expected Arm architecture, got {arch}"
55+
```
56+
57+
Run this script:
58+
59+
```bash
60+
python verify_arch.py
61+
```
62+
63+
The output confirms the container runs on Arm:
64+
65+
```output
66+
Container architecture: aarch64
67+
```
68+
69+
## Start the MCP server container
70+
71+
Now apply these concepts to the Arm MCP server. Create a script called `mcp_container_example.py`:
72+
73+
```python
74+
from testcontainers.core.container import DockerContainer
75+
from testcontainers.core.waiting_utils import wait_for_logs
76+
77+
MCP_IMAGE = "arm-mcp:latest"
78+
79+
print("Starting MCP server container...")
80+
81+
with (
82+
DockerContainer(MCP_IMAGE)
83+
.with_kwargs(stdin_open=True, tty=False)
84+
) as container:
85+
# Wait for the MCP server to initialize
86+
wait_for_logs(container, "Starting MCP server", timeout=60)
87+
print("MCP server is ready!")
88+
89+
# Get container details
90+
container_id = container.get_wrapped_container().id[:12]
91+
print(f"Container ID: {container_id}")
92+
93+
# The container is now ready for MCP communication
94+
print("Container started successfully. Exiting...")
95+
96+
print("Container automatically stopped and removed.")
97+
```
98+
99+
Run the example:
100+
101+
```bash
102+
python mcp_container_example.py
103+
```
104+
105+
The output shows the container lifecycle:
106+
107+
```output
108+
Starting MCP server container...
109+
MCP server is ready!
110+
Container ID: a1b2c3d4e5f6
111+
Container started successfully. Exiting...
112+
Container automatically stopped and removed.
113+
```
114+
115+
## Understand the container lifecycle
116+
117+
The following diagram illustrates how Testcontainers manages the complete container lifecycle:
118+
119+
![Container lifecycle flowchart showing: Start Test leads to Create DockerContainer, which checks if Image exists. If No, it Pulls image then Creates container. If Yes, it directly Creates container. Then it Starts container, Waits for ready signal, Runs test code, Stops container, Removes container, and finally Test complete.#center](container-lifecycle.png "Figure 2. Testcontainers Container Lifecycle")
120+
121+
The `DockerContainer` context manager handles four phases automatically:
122+
123+
| Phase | What happens |
124+
|-------|--------------|
125+
| Create | Pulls the image if needed, creates the container |
126+
| Start | Starts the container with specified configuration |
127+
| Wait | Blocks until the container is ready (log message appears) |
128+
| Cleanup | Stops and removes the container when exiting the `with` block |
129+
130+
This ensures tests always start with a clean environment and never leave orphaned containers.
131+
132+
## Configure container options
133+
134+
Testcontainers provides methods to configure container settings:
135+
136+
```python
137+
from testcontainers.core.container import DockerContainer
138+
139+
with (
140+
DockerContainer("arm-mcp:latest")
141+
.with_env("LOG_LEVEL", "debug") # Set environment variables
142+
.with_volume_mapping("/local/path", "/container/path") # Mount volumes
143+
.with_kwargs(stdin_open=True, tty=False) # Additional Docker options
144+
) as container:
145+
# Container is configured and running
146+
pass
147+
```
148+
149+
Common configuration options include:
150+
151+
- **with_env()**: Set environment variables inside the container
152+
- **with_volume_mapping()**: Mount host directories into the container
153+
- **with_kwargs()**: Pass additional arguments to the Docker SDK
154+
155+
For MCP testing, `stdin_open=True` enables communication over the stdio transport.
156+
157+
## Handle container startup failures
158+
159+
If the MCP server image doesn't exist, Testcontainers raises an error. Add error handling to provide helpful messages:
160+
161+
```python
162+
from testcontainers.core.container import DockerContainer
163+
from testcontainers.core.waiting_utils import wait_for_logs
164+
from docker.errors import ImageNotFound
165+
166+
MCP_IMAGE = "arm-mcp:latest"
167+
168+
try:
169+
with (
170+
DockerContainer(MCP_IMAGE)
171+
.with_kwargs(stdin_open=True)
172+
) as container:
173+
wait_for_logs(container, "Starting MCP server", timeout=60)
174+
print("MCP server started successfully")
175+
except ImageNotFound:
176+
print(f"Error: Image '{MCP_IMAGE}' not found.")
177+
print("Run 'docker buildx build -f mcp-local/Dockerfile -t arm-mcp .' to build it.")
178+
except TimeoutError:
179+
print("Error: MCP server failed to start within 60 seconds.")
180+
print("Check the Docker logs for more details.")
181+
```
182+
183+
## What you've accomplished and what's next
184+
185+
In this section:
186+
- You ran basic Testcontainers examples to understand the container lifecycle.
187+
- You verified container architecture matches your Arm system.
188+
- You started the MCP server container and waited for it to initialize.
189+
- You learned how to configure containers and handle errors.
190+
191+
In the next section, you will write full integration tests that communicate with the MCP server using the JSON-RPC protocol.

content/learning-paths/cross-platform/automate-mcp-with-testcontainers/setup-environment.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,4 @@ In this section:
129129
- You set up a Python virtual environment with Pytest and Testcontainers.
130130
- You explored the test directory structure.
131131

132-
In the next section, you will examine the test code and understand how to write integration tests for MCP servers.
132+
In the next section, you will run basic Testcontainers examples to understand how containers are managed programmatically.

0 commit comments

Comments
 (0)