Testing Charts: helm test and the ct Tool
Ship charts with confidence using in-cluster test hooks (helm test) and the chart-testing CLI (ct) for lint, install, and upgrade checks in CI.
Untested charts rot quietly. A value gets renamed, a template drifts, and you find out during a production upgrade. Two layers of testing keep that from happening.
Layer one: helm test (in-cluster smoke tests)
A chart can ship its own tests as Pods annotated with helm.sh/hook: test. After you install a release, helm test <release> runs those Pods against the live deployment — can the service actually be reached? does the DB connection open? A test that exits 0 passes; non-zero fails. This is the difference between "the manifests applied" and "the app actually works."
Layer two: ct (chart-testing in CI)
The ct CLI from the Helm project is built for pull-request pipelines. It detects which charts changed, enforces that version was bumped, runs ct lint against multiple value files, then ct install spins the chart up in a throwaway cluster (kind works great) and runs the helm test hooks. ct install --upgrade even installs the previous released version first and upgrades to your PR — catching the upgrade breakages that plain installs miss.
The rule that catches the most bugs: block any PR that changes a chart but forgets to bumpversioninChart.yaml.ctenforces this for free.
Example
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "mychart.fullname" . }}-test"
annotations:
helm.sh/hook: test
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
restartPolicy: Never
containers:
- name: wget
image: busybox:1.36
command: ['wget']
args:
- '--spider' # HEAD request, exit non-zero on failure
- '{{ include "mychart.fullname" . }}:{{ .Values.service.port }}'
---
# Run it after install: helm test my-web
# In CI: ct lint --charts ./mychart && ct install --charts ./mychart --upgradeWhen to use it
- A chart maintainer runs helm test after every staging install in CI to confirm the deployed service responds to health checks before marking the pipeline green.
- A platform team uses the chart-testing (ct) CLI to lint and install all charts in their monorepo on every PR, only testing charts whose files changed.
- A developer adds a connection-test Pod as a Helm test hook so any user who installs the chart can run helm test to quickly verify their deployment is functioning.
More examples
Run built-in chart tests
Installs or upgrades a release, waits for readiness, then runs all test hook Pods and prints their logs to verify the deployment.
helm upgrade --install my-app ./myapp \
-f values-staging.yaml \
--wait -n staging
helm test my-app -n staging --logsInstall chart-testing tool
Installs the ct CLI and runs lint against charts that changed versus the main branch, avoiding unnecessary testing of unchanged charts.
# Install ct (chart-testing)
pip3 install yamllint
curl -fsSL https://github.com/helm/chart-testing/releases/download/v3.10.1/chart-testing_3.10.1_linux_amd64.tar.gz | tar xz
mv ct /usr/local/bin/
# Lint all changed charts
ct lint --target-branch mainct install in CI
Uses ct install to install only the changed charts into a temporary namespace, running their tests and cleaning up after.
# .github/workflows/ci.yaml snippet (run in bash)
ct install \
--target-branch main \
--chart-dirs charts/ \
--namespace ct-testing \
--create-namespace
Discussion