Automate MCP server testing using Pytest and Testcontainers
Introduction
Introduction to MCP server testing
Set up your testing environment
Run a basic Testcontainers example
Write integration tests for MCP servers
Configure GitHub Actions for CI/CD
Next Steps
Automate MCP server testing using Pytest and Testcontainers
Why use GitHub Actions for MCP testing?
GitHub Actions provides automated CI/CD directly in your repository. For MCP server testing, it offers:
- Arm runner support: GitHub provides native Arm64 runners for building and testing
- Docker integration: Runners come with Docker pre-installed
- Automatic triggers: Tests run on every push and pull request
- Parallel execution: Multiple jobs can run simultaneously
Create the workflow file
Create a GitHub Actions workflow file at .github/workflows/integration-tests.yml:
name: Integration Tests
on:
push:
pull_request:
jobs:
integration-tests:
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
- name: Install test dependencies
run: |
python -m pip install --upgrade pip
pip install -r mcp-local/tests/requirements.txt
- name: Build MCP Docker image
run: docker buildx build -f mcp-local/Dockerfile -t arm-mcp .
- name: Run integration tests
env:
MCP_IMAGE: arm-mcp:latest
run: pytest -v mcp-local/tests/test_mcp.py
Understand the workflow configuration
The workflow uses the ubuntu-24.04-arm runner, which is a GitHub-hosted Arm64 runner. This ensures that both the Docker build and the tests execute natively on Arm hardware.
Key aspects of the configuration:
Trigger events
on:
push:
pull_request:
This configuration triggers the workflow on every push to any branch and on pull request events. You can restrict this to specific branches if needed:
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
Python caching
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
The cache: pip option caches Python packages between runs, significantly speeding up subsequent workflow executions.
Environment variables
- name: Run integration tests
env:
MCP_IMAGE: arm-mcp:latest
run: pytest -v mcp-local/tests/test_mcp.py
The MCP_IMAGE environment variable tells the test suite which Docker image to use. The test code reads this with:
image = os.getenv("MCP_IMAGE", constants.MCP_DOCKER_IMAGE)
Add test result artifacts
Enhance the workflow to save test results as artifacts for debugging:
- name: Run integration tests
env:
MCP_IMAGE: arm-mcp:latest
run: pytest -v mcp-local/tests/test_mcp.py --junitxml=test-results.xml
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: test-results.xml
The if: always() condition ensures test results upload even when tests fail.
Add a build matrix for multiple platforms
To test on both Arm64 and x86_64, use a matrix strategy:
jobs:
integration-tests:
strategy:
matrix:
runner: [ubuntu-24.04-arm, ubuntu-latest]
runs-on: ${{ matrix.runner }}
steps:
# ... same steps as before
This runs the integration tests in parallel on both architectures.
Monitor workflow runs
After you push the workflow file, navigate to the Actions tab in your GitHub repository. Each workflow run shows:
- Build steps and their status
- Execution time for each step
- Log output for debugging failures
- Artifacts for download
Troubleshoot common issues
If the workflow fails, check these common causes:
Docker build timeout: The initial image build can take more than 10 minutes. GitHub Actions has a default timeout of 360 minutes per job, but individual steps might need explicit timeouts:
- name: Build MCP Docker image
timeout-minutes: 30
run: docker buildx build -f mcp-local/Dockerfile -t arm-mcp .
Container startup issues: If tests fail with timeout errors, the MCP server might not be starting correctly. Add debug output:
- name: Run integration tests
env:
MCP_IMAGE: arm-mcp:latest
run: |
docker run --rm arm-mcp:latest echo "Container starts successfully"
pytest -v -s mcp-local/tests/test_mcp.py
Rate limiting: If tests query external services, you might encounter rate limits. Consider adding retry logic or using mock responses for CI environments.
What you’ve accomplished and what’s next
In this Learning Path, you learned how to:
- Set up Testcontainers for Docker-based integration testing
- Write pytest tests that communicate with MCP servers over stdio transport
- Parse MCP JSON-RPC responses and validate tool outputs
- Configure GitHub Actions with Arm64 runners for automated testing
These techniques apply to any MCP server implementation, not just the Arm MCP Server. Use this foundation to build comprehensive test suites that ensure your MCP tools work correctly across updates and deployments.