Тестирование Helm-чартов
Updated Jul 2026
Почему Helm-чарты нуждаются в собственных тестах
Helm-чарты добавляют слой шаблонизации поверх манифестов Kubernetes. Это порождает новый класс ошибок: шаблон может генерировать валидный YAML для одного набора значений, но невалидный — для другого. Условный блок может тихо пропустить критический ресурс. Вспомогательный шаблон может сгенерировать некорректные лейблы, которые нарушат обнаружение сервисов.
Тестирование Helm-чартов означает валидацию не только самого чарта, но и результатов рендеринга для всех поддерживаемых комбинаций значений.
Три уровня тестирования Helm
| Уровень | Что тестирует | Скорость | Инструменты |
|---|---|---|---|
| Линтинг | Структура чарта, синтаксис, метаданные | Мгновенно | helm lint |
| Рендеринг шаблонов + валидация | Корректность отрендеренного YAML | Секунды | helm template + kubeconform |
| Модульные тесты | Логика шаблонов, условия, значения по умолчанию | Секунды | helm-unittest |
| Интеграционные тесты | Чарт разворачивается и работает в реальном кластере | Минуты | helm test, ct (chart-testing) |
Линтинг: первый рубеж
helm lint выявляет структурные проблемы в вашем чарте до рендеринга или развёртывания:
# Basic lint
helm lint ./charts/myapp/
# Lint with specific values (catches issues with value-dependent templates)
helm lint ./charts/myapp/ --values values-production.yaml
# Lint with strict mode (warnings become errors)
helm lint ./charts/myapp/ --strict
# Common issues helm lint catches:
# - Missing Chart.yaml required fields
# - Template syntax errors (unclosed {{ }})
# - Missing values referenced in templates
# - Deprecated API versions
Лучшие практики Chart.yaml
# Chart.yaml
apiVersion: v2
name: myapp
description: A Helm chart for the MyApp service
type: application
version: 1.5.0 # Chart version (SemVer)
appVersion: "2.3.1" # Application version
maintainers:
- name: Platform Team
email: platform@example.com
dependencies:
- name: postgresql
version: "13.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
Рендеринг шаблонов и валидация
Рендеринг шаблонов без развёртывания — наиболее важный этап тестирования. Он выявляет проблемы, которые линтинг не может обнаружить, поскольку линтинг не выполняет логику шаблонов.
# Render templates with default values
helm template myapp ./charts/myapp/
# Render with production values
helm template myapp ./charts/myapp/ \
--values values-production.yaml
# Pipe rendered output to kubeconform for K8s schema validation
helm template myapp ./charts/myapp/ \
--values values-production.yaml | kubeconform -strict
# Test with multiple value files
helm template myapp ./charts/myapp/ \
--values values-production.yaml \
--values values-us-east-1.yaml | kubeconform -strict
# Render with value overrides to test specific scenarios
helm template myapp ./charts/myapp/ \
--set replicas=1 \
--set image.tag=latest \
--set ingress.enabled=true | kubeconform -strict
Тестирование всех комбинаций значений
#!/bin/bash
# scripts/test-helm-values.sh
# Test chart rendering with all supported value files
CHART_DIR="./charts/myapp"
FAILURES=0
# Test each environment's values
for values_file in values-*.yaml; do
echo "Testing with $values_file..."
if ! helm template myapp "$CHART_DIR" --values "$values_file" | kubeconform -strict; then
echo "FAIL: $values_file produces invalid manifests"
FAILURES=$((FAILURES + 1))
fi
done
# Test with minimal values (defaults only)
echo "Testing with defaults only..."
if ! helm template myapp "$CHART_DIR" | kubeconform -strict; then
echo "FAIL: Default values produce invalid manifests"
FAILURES=$((FAILURES + 1))
fi
if [ $FAILURES -gt 0 ]; then
echo "$FAILURES value combinations failed"
exit 1
fi
echo "All value combinations passed"
Модульное тестирование с helm-unittest
helm-unittest — это специализированный фреймворк модульного тестирования для Helm-чартов. Он позволяет писать утверждения против результата рендеринга шаблонов без развёртывания.
Установка
# Install as a Helm plugin
helm plugin install https://github.com/helm-unittest/helm-unittest
Структура тестов
charts/myapp/
Chart.yaml
values.yaml
templates/
deployment.yaml
service.yaml
ingress.yaml
tests/
deployment_test.yaml
service_test.yaml
ingress_test.yaml
Написание модульных тестов
# tests/deployment_test.yaml
suite: Deployment Tests
templates:
- deployment.yaml
tests:
- it: should set resource limits
asserts:
- isNotNull:
path: spec.template.spec.containers[0].resources.limits.cpu
- isNotNull:
path: spec.template.spec.containers[0].resources.limits.memory
- it: should not run as root
asserts:
- equal:
path: spec.template.spec.securityContext.runAsNonRoot
value: true
- it: should use the correct image tag
set:
image.tag: "2.3.1"
asserts:
- matchRegex:
path: spec.template.spec.containers[0].image
pattern: ":2\\.3\\.1$"
- it: should set correct replica count for production
values:
- ../values-production.yaml
asserts:
- equal:
path: spec.replicas
value: 3
- it: should set readiness probe
asserts:
- isNotNull:
path: spec.template.spec.containers[0].readinessProbe
- equal:
path: spec.template.spec.containers[0].readinessProbe.httpGet.path
value: /healthz
- it: should set liveness probe
asserts:
- isNotNull:
path: spec.template.spec.containers[0].livenessProbe
- it: should drop all capabilities
asserts:
- contains:
path: spec.template.spec.containers[0].securityContext.capabilities.drop
content: "ALL"
- it: should not use latest tag
set:
image.tag: "latest"
asserts:
- failedTemplate:
errorMessage: "image.tag must not be 'latest'"
Тестирование условных ресурсов
# tests/ingress_test.yaml
suite: Ingress Tests
templates:
- ingress.yaml
tests:
- it: should not create ingress by default
asserts:
- hasDocuments:
count: 0
- it: should create ingress when enabled
set:
ingress.enabled: true
ingress.host: "myapp.example.com"
asserts:
- hasDocuments:
count: 1
- equal:
path: spec.rules[0].host
value: "myapp.example.com"
- it: should configure TLS when specified
set:
ingress.enabled: true
ingress.host: "myapp.example.com"
ingress.tls.enabled: true
ingress.tls.secretName: "myapp-tls"
asserts:
- equal:
path: spec.tls[0].secretName
value: "myapp-tls"
- contains:
path: spec.tls[0].hosts
content: "myapp.example.com"
Запуск модульных тестов
# Run all tests
helm unittest ./charts/myapp/
# Run with verbose output
helm unittest -v ./charts/myapp/
# Output as JUnit XML for CI
helm unittest -o junit -f test-results.xml ./charts/myapp/
# Run specific test file
helm unittest -f 'tests/deployment_test.yaml' ./charts/myapp/
Интеграционное тестирование с chart-testing (ct)
chart-testing (ct) от сопровождающих Helm валидирует чарты, фактически устанавливая их в кластер. Используйте его с kind (Kubernetes in Docker) для CI:
# .github/workflows/helm-test.yml
name: Helm Chart Tests
on:
pull_request:
paths:
- 'charts/**'
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Helm
uses: azure/setup-helm@v4
- name: Set up chart-testing
uses: helm/chart-testing-action@v2
- name: Lint charts
run: ct lint --all
- name: Create kind cluster
uses: helm/kind-action@v1
- name: Install and test charts
run: ct install --all
Тестовые хуки в чарте
Helm поддерживает тестовые хуки — поды, которые запускаются после установки для проверки работоспособности чарта:
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "myapp.fullname" . }}-test-connection"
labels:
{{- include "myapp.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": hook-succeeded
spec:
containers:
- name: wget
image: busybox:1.36
command: ['wget']
args: ['{{ include "myapp.fullname" . }}:{{ .Values.service.port }}/healthz']
restartPolicy: Never
# Run chart tests after installation
helm test myapp --namespace production
Итоговые лучшие практики
- Всегда линтуйте перед рендерингом --
helm lint --strictмгновенно выявляет структурные проблемы. - Рендерите со всеми файлами значений -- Чарт, который работает с настройками по умолчанию, но ломается с продакшен-значениями, представляет риск при развёртывании.
- Передавайте результат рендеринга в kubeconform -- Это выявляет проблемы со схемой Kubernetes, которые Helm не проверяет.
- Пишите модульные тесты для условий -- Каждый
if/elseв ваших шаблонах нуждается в тесте для обеих ветвей. - Тестируйте валидацию значений -- Если определённые комбинации значений невалидны, убедитесь, что шаблоны падают с понятным сообщением об ошибке.
- Используйте chart-testing в CI -- Реальная установка выявляет проблемы, которые статический анализ пропускает.
- Фиксируйте версии зависимостей -- Используйте точные или диапазонные версии в Chart.yaml, никогда
*.