调试容器化的Go项目测试
我有一个用Go语言编写的项目,是一个用React构建的单页应用,其中Go语言充当静态文件服务器和API。该应用可以在web文件夹中通过 docker compose up 和 npm run 启动。
以下是 compose.yaml:
services:
server:
container_name: hello-world-server
build:
context: .
target: development
volumes:
- .:/app
- gomodcache:/go/pkg/mod
ports:
- 40531:8080
- 40532:12345
env_file: .env
depends_on:
- db
db:
container_name: hello-world-db
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: postgres
PGUSER: postgres
ports:
- 40533:5432
volumes:
- ./initdb:/docker-entrypoint-initdb.d
- dbdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready"]
interval: 1s
timeout: 10s
retries: 10
adminer:
container_name: hello-world-adminer
image: adminer:latest
restart: always
ports:
- 40534:8080
volumes:
gomodcache: null
dbdata: null
以下是 Dockerfile:
FROM golang:1.25-alpine AS base
WORKDIR /app
FROM base AS development
RUN apk add build-base
# Create external debugger
RUN go install github.com/go-delve/delve/cmd/dlv@latest
# Restart server on code change
RUN go install github.com/cespare/reflex@latest
COPY reflex.conf /
ENTRYPOINT ["reflex", "-c", "/reflex.conf"]
FROM node:25-alpine AS node-builder
WORKDIR /app
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure copying both package.json AND package-lock.json (when available).
# Copying this first prevents re-running npm install on every code change.
COPY web/package*.json ./
RUN npm ci
COPY web .
RUN npm run build
FROM base AS go-builder
# Retrieve application dependencies.
# This allows the container build to reuse cached dependencies.
# Expecting to copy go.mod and if present go.sum.
COPY go.* ./
RUN go mod download
# Copy local code to the container image.
COPY . .
COPY --from=node-builder /app/dist /app/web/dist
# Build the binary.
RUN go build -buildvcs=false -v -o server
FROM alpine:latest
# Copy the binary to the production image from the builder stage.
COPY --from=go-builder /app/server /app/server
# Run the web service on container startup.
CMD ["/app/server"]
使用 reflex.config 和 launch.json 对HTTP调用进行调试,效果如预期。
我需要添加并调试一些测试。在YAML文件中我添加了一个服务:
test:
container_name: hello-world-test
build:
context: .
target: test
volumes:
- .:/app
- gomodcache:/go/pkg/mod
env_file: .env
depends_on:
- db
在Dockerfile中添加了一个阶段:
FROM base AS test
# Copy Go modules
COPY go.* ./
RUN go mod download
# Copy full source code
COPY . .
# Default command to run tests
CMD ["go", "test", "./...", "-v"]
我可以使用命令 docker-compose run test 运行测试,但无法在调试模式下运行。
如何调试我的测试?如何附加调试器?我只使用VS Code。
解决方案
我已经搞定了,只要按F5就能完成所有操作。
- 在compose.yaml中定义服务
test:
container_name: hello-world-test
build:
context: .
target: test
volumes:
- .:/app
- gomodcache:/go/pkg/mod
env_file: .env
depends_on:
- db
ports:
- 40535:12345
security_opt:
- seccomp:unconfined
cap_add:
- SYS_PTRACE
profiles:
- test
2. Dockerfile中的阶段
FROM base AS test
RUN apk add build-base
RUN go install github.com/go-delve/delve/cmd/dlv@latest
COPY go.* ./
RUN go mod download
3.定义任务 (.vscode/task.json)
{
"version": "2.0.0",
"tasks": [
{
"label": "docker-test-up",
"type": "shell",
"command": "docker compose --profile test up test --build",
"isBackground": true,
"presentation": {
"panel": "shared",
"group": "test"
},
"problemMatcher": {
"pattern": { "regexp": "." },
"background": {
"activeOnStart": true,
"beginsPattern": "Attaching to",
"endsPattern": "API server listening"
}
}
},
{
"label": "docker-test-down",
"type": "shell",
"command": "docker compose --profile test down",
"presentation": {
"panel": "shared",
"group": "test",
"close": true
}
}
]
}
4.定义调试配置 (.vscode/launch.json)
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach to container",
"type": "go",
"request": "attach",
"mode": "remote",
"debugAdapter": "dlv-dap",
"port": 40532,
"substitutePath": [{ "from": "${workspaceFolder}", "to": "/app" }]
},
{
"name": "Attach to tests",
"type": "go",
"request": "attach",
"mode": "remote",
"debugAdapter": "dlv-dap",
"port": 40535,
"substitutePath": [{ "from": "${workspaceFolder}", "to": "/app" }],
"preLaunchTask": "docker-test-up",
"postDebugTask": "docker-test-down"
}
]
}
按下F5时,VS Code会尝试将调试器附加到进程,preLaunchTask 会构建镜像并运行容器。测试将被执行,最后 postDebugTask 会停止并移除容器。多亏了任务中的 presentation 属性,所有输出都在同一个面板中显示。