Modern QA2026Тестирование Helm-чартов
Join

Course08 Infrastructure as Code Testing

Cutting-edge · Chapter 08

Тестирование 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

Итоговые лучшие практики

  1. Всегда линтуйте перед рендерингом -- helm lint --strict мгновенно выявляет структурные проблемы.
  2. Рендерите со всеми файлами значений -- Чарт, который работает с настройками по умолчанию, но ломается с продакшен-значениями, представляет риск при развёртывании.
  3. Передавайте результат рендеринга в kubeconform -- Это выявляет проблемы со схемой Kubernetes, которые Helm не проверяет.
  4. Пишите модульные тесты для условий -- Каждый if/else в ваших шаблонах нуждается в тесте для обеих ветвей.
  5. Тестируйте валидацию значений -- Если определённые комбинации значений невалидны, убедитесь, что шаблоны падают с понятным сообщением об ошибке.
  6. Используйте chart-testing в CI -- Реальная установка выявляет проблемы, которые статический анализ пропускает.
  7. Фиксируйте версии зависимостей -- Используйте точные или диапазонные версии в Chart.yaml, никогда *.