Stage 3 · Build
Kubernetes Controllers in Go
Testing Controllers
envtest, ginkgo, and integration testing with kind for reliable Kubernetes controllers.
Testing Pyramid
Controller testing follows a pyramid: many fast unit tests with envtest, fewer integration tests with kind, and minimal end-to-end tests. Each level provides different confidence at different speeds.
- Unit tests — Test individual functions with mocks
- envtest tests — Test reconcile logic with a real API server (no etcd)
- Integration tests — Test with kind cluster (full Kubernetes stack)
- E2E tests — Test with a real cluster (slowest, highest confidence)
envtest for Unit Tests
envtest runs a real API server and controller-manager without needing a full cluster. It is fast enough for unit tests and realistic enough to catch integration issues.
Ginkgo/Gomega
Ginkgo is a BDD testing framework for Go. It provides describe/it blocks, setup/teardown, and parallel execution. Gomega provides matchers for assertions.
Integration with kind
kind (Kubernetes in Docker) runs a real Kubernetes cluster in Docker. Integration tests verify your controller works with real etcd, real API server, and real kubelet.
# Create a kind cluster
kind create cluster --name test --wait 5m
# Install CRDs
kubectl apply -f config/crd
# Build and load the operator image
make docker-build IMG=my-operator:test
kind load docker-image my-operator:test --name test
# Deploy the operator
kubectl create namespace my-operator-system
IMG=my-operator:test make deploy
# Run integration tests
go test -tags=integration ./tests/integration/...
# Cleanup
kind delete cluster --name testkind provides a real cluster for testing. load docker-image avoids pushing to a registry. The integration tests deploy your operator and create test resources. This catches issues that envtest misses.
Mocking the API Server
For fast unit tests, mock the client.Client interface. This lets you test reconcile logic without any Kubernetes dependencies.
Coverage Strategies
Controller coverage focuses on the reconcile function. Test success paths, error paths, not-found scenarios, and status updates. Aim for high coverage on the reconcile function, moderate coverage on helpers.
# Unit test coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
# Focus on controller coverage
go test -coverprofile=coverage.out ./internal/controller/
go tool cover -func=coverage.out | grep -E "(controller|total)"Focus coverage efforts on the reconcile function — that is where the business logic lives. Test every code path: create, update, delete, not-found, error conditions. The coverage report shows which lines are not tested.
Use envtest for most controller tests. Use fake client for pure logic tests. Use kind for integration tests that need real cluster behavior. E2E tests should only verify the critical paths that matter to users.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.