Introduction: Why Docker and Swift are the Perfect Pair for Integration Testing?
\n\nModern iOS and macOS development requires not only unit tests but also full-fledged integration testing. When your application interacts with a database, external APIs, or the file system, isolated testing of each component is no longer sufficient. This is where Docker comes to the rescue.
\n\nDocker allows you to create identical environments for tests, eliminating the "it works on my machine" problem. For Swift developers, this is especially relevant: containerization makes it possible to test server-side Swift applications (Vapor, Kitura), as well as emulate infrastructure for iOS applications (databases, caches, message queues).
\n\nIn this article, we will cover the complete cycle of setting up integration testing for Swift with Docker: from creating a test environment to running in CI/CD.
\n\n1. Environment Setup: Docker and Swift
\n\n1.1. Installing Docker and Swift Toolchain
\n\nFirst, make sure you have installed:
\n- \n
- Docker Desktop (for macOS) or Docker Engine (for Linux) \n
- Swift Toolchain version 5.7+ (download from swift.org) \n
- Xcode 14+ (if testing iOS components) \n
1.2. Creating a Dockerfile for Swift
\n\nLet's start by creating a base Docker image for our Swift application. Create a Dockerfile in the project root:
# Dockerfile\nFROM swift:5.9-focal AS base\n\n# Set the working directory\nWORKDIR /app\n\n# Copy the Package.swift manifest file\nCOPY Package.swift ./\nCOPY Sources/ ./Sources/\nCOPY Tests/ ./Tests/\n\n# Build the project\nRUN swift build -c debug\n\n# Final image for tests\nFROM base AS test\nCMD ["swift", "test", "--parallel"]\n\n\nThis image builds the project and runs tests. For integration tests, we will need additional services.
\n\n2. Integration Testing with Docker Compose
\n\nDocker Compose allows you to run multiple containers simultaneously. For a typical scenario, we will need:
\n- \n
- A container with our Swift application \n
- A container with PostgreSQL (or another database) \n
- A container with Redis (for caching) \n
2.1. Example docker-compose.yml
\n\n# docker-compose.yml\nversion: '3.8'\n\nservices:\n postgres:\n image: postgres:15\n environment:\n POSTGRES_USER: test_user\n POSTGRES_PASSWORD: test_pass\n POSTGRES_DB: test_db\n ports:\n - "5432:5432"\n healthcheck:\n test: ["CMD-SHELL", "pg_isready -U test_user -d test_db"]\n interval: 5s\n timeout: 5s\n retries: 5\n\n redis:\n image: redis:7-alpine\n ports:\n - "6379:6379"\n\n swift-tests:\n build:\n context: .\n dockerfile: Dockerfile.test\n depends_on:\n postgres:\n condition: service_healthy\n redis:\n condition: service_started\n environment:\n DATABASE_URL: "postgres://test_user:test_pass@postgres:5432/test_db"\n REDIS_URL: "redis://redis:6379"\n volumes:\n - .:/app\n\n\n2.2. Special Dockerfile for Tests
\n\nLet's create a Dockerfile.test that will wait for dependencies to be ready:
# Dockerfile.test\nFROM swift:5.9-focal\n\nWORKDIR /app\n\n# Install netcat for connection checks\nRUN apt-get update && apt-get install -y netcat-openbsd && rm -rf /var/lib/apt/lists/*\n\nCOPY Package.swift ./\nRUN swift package resolve\n\nCOPY . .\n\n# Wait for PostgreSQL and Redis\nCOPY wait-for-services.sh /usr/local/bin/\nRUN chmod +x /usr/local/bin/wait-for-services.sh\n\nCMD ["wait-for-services.sh"]\n\n\nThe wait-for-services.sh script:
#!/bin/bash\n# wait-for-services.sh\n\necho "Waiting for PostgreSQL..."\nwhile ! nc -z postgres