helm-chart-testing
Skill19 starsChanged 10 months agoMITView on GitHub
Use when tests need to be added to a Helm chart, Helm tests are failing with unclear causes, or a chart needs test coverage analysis
In this repository19 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- cert-manager-troubleshootingSkill
- external-dns-troubleshootingSkill
- external-secrets-troubleshootingSkill
- flux-gitops-patternsSkill
- flux-operationsSkill
- flux-troubleshootingSkill
- helm-chart-developmentSkill
- helm-chart-maintenanceSkill
- helm-chart-reviewSkill
- k8s-continual-improvementSkill
- k8s-dns-troubleshootingSkill
- k8s-namespace-troubleshootingSkill
- k8s-network-troubleshootingSkill
- k8s-platform-operationsSkill
- k8s-platform-tenancySkill
- k8s-security-hardeningSkill
- k8s-security-redteamSkill
- kyverno-troubleshootingSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 8
Write effective built-in Helm tests using pod annotations that run with helm test.
- Very specific: line 42
Run tests with helm test RELEASE_NAME
- Distinctive: line 109
Use lightweight images (curlimages/curl, busybox, alpine)
- Very specific: line 392
Use helm.sh/hook-weight (lower runs first):
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Helm Chart Testing
Write effective built-in Helm tests using pod annotations that run with helm test. Focus on designing tests that verify releases work correctly.
Keywords
helm test, helm.sh/hook, test-success, test-failure, pod test, test annotation, smoke test, connectivity test, configuration validation, release verification, failing test, test coverage
When to Use This Skill
- Adding test pods to a Helm chart
- Diagnosing why Helm tests are failing
- Determining what needs testing in a chart
- Writing connectivity, configuration, or readiness tests
- Designing test strategy for complex charts
Related Skills
- helm-chart-development - Chart structure and templating
- helm-chart-maintenance - Testing in CI/CD pipelines
- k8s-platform-operations - Debugging test failures
Quick Reference
| Task | Command/Annotation |
| Run tests | helm test RELEASE_NAME |
| Run with logs | helm test RELEASE_NAME --logs |
| Mark pod as test | helm.sh/hook: test-success |
| Keep test pods | helm.sh/hook-delete-policy: never |
| Order execution | helm.sh/hook-weight: "-5" |
How Helm Tests Work
- Define test pods in
templates/withhelm.sh/hook: testannotation - Install chart with
helm installorhelm upgrade - Run tests with
helm test RELEASE_NAME - Helm creates pods, executes them, reports results
Test passes: Pod exits with code 0 Test fails: Pod exits with non-zero code
Test Annotations
| Annotation | Purpose |
helm.sh/hook: test | Marks pod as a test (runs during helm test) |
helm.sh/hook: test-success | Runs after successful release |
helm.sh/hook: test-failure | Runs after failed release |
helm.sh/hook-weight: | Controls execution order (lower = first) |
helm.sh/hook-delete-policy: | Controls cleanup (hook-succeeded, never) |
Test Analysis: What to Test
| Resource Type | Test Considerations |
| Services | Endpoint reachability, DNS, correct ports |
| Deployments/StatefulSets | Pod readiness, replica count, rollout status |
| Ingress | Route reachability, TLS certificates |
| ConfigMaps/Secrets | Values present, mounted correctly |
| PVCs | Volume mounted, read/write access |
| CRDs | Custom resource creation, reconciliation |
Analysis questions:
- What is the minimum viable verification that the release worked?
- What are the critical failure modes?
- What external dependencies exist?
- Can tests run safely in production?
Test Categories
| Category | Purpose | Examples |
| Smoke | Quick "is it alive" | Service health, pod readiness |
| Functional | Verify specific behavior | API responses, database connectivity |
| Integration | Verify external interactions | Upstream services, third-party APIs |
| Data Validation | Verify deployed state | ConfigMap content, environment variables |
Basic Test Pod Structure
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-test-connectivity"
annotations:
helm.sh/hook: test-success
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
containers:
- name: test
image: curlimages/curl:8.7.1
command:
- sh
- -c
- |
set -e
curl -f http://myapp-service:8080/health
restartPolicy: NeverBest practices:
- Use
restartPolicy: Never - Use lightweight images (curlimages/curl, busybox, alpine)
- One test pod tests one thing well
- Exit code 0 = pass, non-zero = fail
Common test images (always pin to a specific tag, not latest):
curlimages/curl:8.7.1- HTTP endpoint checksbusybox:1.36- Basic shell utilitiesbitnami/kubectl:1.29- Kubernetes API queriespostgres:16-alpine,mysql:8.0-oracle- Database connectivity
Test Examples
Service Connectivity Test
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-api-test"
annotations:
helm.sh/hook: test-success
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
containers:
- name: api-test
image: curlimages/curl:8.7.1
command:
- sh
- -c
- |
set -e
# Test main endpoint
curl -f http://{{ .Release.Name }}-service:8080/health || exit 1
# Verify response content
curl -s http://{{ .Release.Name }}-service:8080/health | grep -q "status.*ok" || exit 1
{{- if .Values.auth.enabled }}
# Test authenticated endpoint
curl -f http://{{ .Release.Name }}-service:8080/secure \
-H "Authorization: Bearer {{ .Values.auth.testToken }}" || exit 1
{{- end }}
restartPolicy: NeverConfiguration Validation Test
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-config-test"
annotations:
helm.sh/hook: test-success
spec:
containers:
- name: config-test
image: busybox:1.36
command:
- sh
- -c
- |
set -e
test -f /app/config.yaml || exit 1
grep -q "logLevel: {{ .Values.logLevel }}" /app/config.yaml || exit 1
grep -q "database:" /app/config.yaml || exit 1
volumeMounts:
- name: config
mountPath: /app/config.yaml
subPath: config.yaml
volumes:
- name: config
configMap:
name: {{ include "myapp.fullname" . }}-config
restartPolicy: NeverDeployment Readiness Test
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-readiness-test"
annotations:
helm.sh/hook: test-success
spec:
serviceAccountName: {{ include "myapp.fullname" . }}-test-sa
containers:
- name: kubectl-test
image: bitnami/kubectl:1.29
command:
- sh
- -c
- |
set -e
kubectl get deployment {{ .Release.Name }} -n {{ .Release.Namespace }} -o json | \
jq -e '.status.readyReplicas == {{ .Values.replicaCount }}' || exit 1
restartPolicy: NeverDatabase Connection Test
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-db-test"
annotations:
helm.sh/hook: test-success
spec:
containers:
- name: db-test
image: postgres:16-alpine
command:
- sh
- -c
- |
set -e
nc -zv {{ .Values.database.host }} {{ .Values.database.port }} || exit 1
PGPASSWORD={{ .Values.database.password }} \
psql -h {{ .Values.database.host }} -p {{ .Values.database.port }} \
-U {{ .Values.database.user }} -d {{ .Values.database.name }} \
-c "SELECT 1;" || exit 1
restartPolicy: NeverTest Design Considerations
Test Independence
Each test should verify one thing well:
# Good: Single focused test
metadata:
name: "{{ .Release.Name }}-test-health"
# Avoid: Tests multiple unrelated things
metadata:
name: "{{ .Release.Name }}-test-everything"Resource Management
Set limits to prevent exhaustion:
spec:
containers:
- name: test
image: curlimages/curl:8.7.1
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128MiCleanup Policies
| Policy | Behavior | Use Case |
hook-succeeded | Delete after passing | Normal operation |
never | Never delete | Debugging |
For debugging:
metadata:
annotations:
helm.sh/hook-delete-policy: neverError Handling
Provide clear failure messages:
command:
- sh
- -c
- |
set -e
if ! curl -f http://service:8080/health; then
echo "ERROR: Service health check failed"
echo "Troubleshooting: kubectl get svc, kubectl logs -l app=myapp"
exit 1
fiSecurity
Use least privilege RBAC:
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "myapp.fullname" . }}-test-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "myapp.fullname" . }}-test-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "myapp.fullname" . }}-test-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ include "myapp.fullname" . }}-test-role
subjects:
- kind: ServiceAccount
name: {{ include "myapp.fullname" . }}-test-saDon't embed secrets:
# Avoid
command: ["curl", "-H", "Authorization: Bearer super-secret-key", "http://service/"]
# Better
env:
- name: TEST_TOKEN
valueFrom:
secretKeyRef:
name: test-credentials
key: tokenOrganizing Tests
mychart/
├── templates/
│ ├── tests/
│ │ ├── test-service-connectivity.yaml
│ │ ├── test-config-validation.yaml
│ │ └── test-readiness.yaml
│ ├── deployment.yaml
│ └── service.yamlConditional Testing
Enable/disable tests globally:
{{- if .Values.tests.enabled }}
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-test"
annotations:
helm.sh/hook: test-success
spec:
# ...
{{- end }}In values.yaml:
tests:
enabled: trueTest specific configurations:
{{- if .Values.metrics.enabled }}
# metrics test
{{- end }}Test Execution Order
Use helm.sh/hook-weight (lower runs first):
# Test 1: Run first
metadata:
annotations:
helm.sh/hook-weight: "-5"
---
# Test 2: Run second
metadata:
annotations:
helm.sh/hook-weight: "0"Running and Debugging
Running Tests
helm test my-release
helm test my-release --logs
helm test my-release --timeout 10m
helm test my-release -n my-namespaceDebugging Failed Tests
kubectl get pods -n namespace -l helm.sh/hook=test
kubectl logs my-release-test-connectivity -n namespace
kubectl describe pod my-release-test-connectivity -n namespaceCommon Test Failures
| Symptom | Cause | Solution |
| Image pull errors | Wrong image/registry | Verify image name and pull secrets |
| Connection refused | Service not ready | Add readiness test, increase timeout |
| Permission denied | Insufficient RBAC | Add service account and role |
| Command not found | Wrong base image | Use image with required tools |
| Timeout | Service startup too slow | Increase timeout or add retry logic |
Anti-Patterns
Don't Test During Installation
# Wrong - runs during install
helm.sh/hook: post-install
# Correct - runs with helm test
helm.sh/hook: test-successDon't Over-Test Platform Guarantees
# Wrong - Kubernetes guarantees this
command: ["kubectl", "get", "pod", "|", "grep", "myapp"]
# Correct - test application functionality
command: ["curl", "http://myapp-service/health"]Avoid Fragile Tests
# Fragile - exact match
response=$(curl http://service/health)
[ "$response" == '{"status":"ok"}' ]
# Robust - content check
curl http://service/health | grep -q "status.*ok"Avoid Long-Running Tests
# Avoid - waits 60 seconds
for i in $(seq 1 60); do sleep 1; done
# Prefer - quick check
curl -f --max-time 10 http://service/healthDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

