helm-unittest is a tool for unit-testing Helm charts. Helm is like M4 for writing Kubernetes manifests, which are YAML files. I prefer M4, because I consider the Go templating language an abomination (mutable data structures, unclear scoping, and so on and so forth). But it’s what’s used in the project, so I don’t really have a choice.

Getting back to unit testing: What helm-unittest does is render a chart and then evaluate some expressions on it to check that expected values are present. This works fine for Helm charts that are “application” charts (the regular type), but not so for “library” charts, which cannot render manifests, and only define helper templates. As I’ve written a rather inscrutable Helm library chart1, I wanted to unit test them.

The Setup

We have a common library chart and two application charts, as follows:

.
├── Chart.lock
├── charts
│   ├── application-chart-one
│   │   ├── charts
│   │   │   └── …
│   │   ├── Chart.yaml
│   │   ├── templates
│   │   │   └── …
│   │   └── values.yaml
│   ├── application-chart-two
│   │   ├── charts
│   │   │   └── …
│   │   ├── Chart.yaml
│   │   ├── templates
│   │   │   └── …
│   │   └── values.yaml
│   └── common-library-chart
│       ├── Chart.yaml
│       └── templates
│           └── _helper_cart.tpl
├── Chart.yaml
└── values.yaml

There are two application charts using the common library chart.

What works

Testing the application charts that use the library chart works fine, but I’d like to keep my testing chart separate from the deployed charts. A github issue in the helm-unittest repository outlined how to do this: We are basically adding a “fake” application chart that lives outside the rest of the charts, and symlinks the common library chart into its templates folder. We can then write unit tests to our heart’s content, and we are sure that the “fake” application chart isn’t deployed. This will not work on Windows (as symlinks might not survive), but seems to work fine in our CI.

The setup thus becomes:

├── toplevel-chart
│   ├── Chart.lock
│   ├── charts
│   │   ├── application-chart-one
│   │   │   ├── charts
│   │   │   │   └── …
│   │   │   ├── Chart.yaml
│   │   │   ├── templates
│   │   │   │   └── …
│   │   │   ├── tests
│   │   │   │   └── snapshot_test.yaml
│   │   │   └── values.yaml
│   │   ├── application-chart-two
│   │   │   ├── charts
│   │   │   │   └── …
│   │   │   ├── Chart.yaml
│   │   │   ├── templates
│   │   │   │   └── …
│   │   │   ├── tests
│   │   │   │   └── snapshot_test.yaml
│   │   │   └── values.yaml
│   │   └── library-chart
│   │       ├── Chart.yaml
│   │       └── templates
│   │           └── _helper_chart.tpl
│   ├── Chart.yaml
│   └── values.yaml
└── library-test-chart
    ├── Chart.lock
    ├── charts
    │   └── library-chart -> ../../toplevel-chart/charts/library-chart
    ├── Chart.yaml
    ├── templates
    │   └── …
    └── tests
        ├── __snapshot__
        ├── test_one.yaml
        ├── test_two.yaml
        └── test_three.yaml

We have some snapshot tests defined in the application charts (that’s the simple part). The library-test-chart depends on the library chart via a symlink. Helm will log that it’s following a symlink, but otherwise it works. Also, the dependency needs to be included explicitly in the test chart’s Chart.yaml with a version for this to work.

Of course, calling “helm unittest .” in the toplevel chart now no longer runs the unit test in the library-test chart, but that can be fixed in the CI and test runner scripts.

What doesn’t work

There were some avenues I tried out that didn’t go anywhere, unfortunately.

Excluding the test chart with .helmignore

Unfortunately, excluding the test chart with .helmignore caused the whole chart to be ignored while rendering. Apparently (and the documentation seems misleading here), this doesn’t just exclude stuff from packaging, but also from rendering.

Using conditions or tags

Specifying a condition or using tags to exclude the testing chart doesn’t work either, because helm-unittest can’t set specific values for testing (there is a -v option for providing a values file, but that doesn’t seem to be used during rendering). The approach still seems promising, but after looking at helm-unittest’s code, there seems to be some involved translation, and the values file doesn’t seem to be used to decide which charts to test.


  1. Not a big hurdle, Helm charts become inscrutable after containing more than 3 lines. ↩︎