Documentation
Testing Templates
cartotest
is a CLI tool to assert that your Cartographer templates behave as you expect.
Quick Start
Install
Clone the Cartographer repository:
git clone git@github.com:vmware-tanzu/cartographer.git
cd cartographer
Install the cartotest CLI:
go install ./cmd/cartotest
Dependency
Users may define templates that use ytt. Testing such templates requires installing ytt.
Run
Run the example template tests:
cartotest templates --directory ./tests/templates/
You should see
PASS: tests/templates/deliverable/regular-template
PASS: tests/templates/deliverable/ytt-preprocess
PASS: tests/templates/deliverable/ytt-template
PASS: tests/templates/kpack
PASS
Great, a passing test!
Failing Tests
Now let’s make a test fail.
The folder ./tests/templates/kpack tests the template.yaml
file. The expected.yaml
file is what we are asserting
should be created by Cartographer. There are two files that are used as inputs to the template. These are
workload.yaml
and info.yaml
.
Let’s alter the workload so that what is stamped out by Cartographer will be different from expected.yaml
.
The example edit uses mikefarah/yq for which you can find installation instructions
here. Alternatively you can manually change the metadata.name
field of
./tests/templates/kpack/workload.yaml
to another-identifier
.
yq '.metadata.name = "another-identifier"' ./tests/templates/kpack/workload.yaml -i
Run cartotests again:
cartotest templates --directory ./tests/templates/
PASS: tests/templates/deliverable/regular-template
PASS: tests/templates/deliverable/ytt-preprocess
PASS: tests/templates/deliverable/ytt-template
FAIL: tests/templates/kpack
FAIL
Let’s get some more detail about the tests by running cartotest in verbose mode:
cartotest templates --directory ./tests/templates/ -v
DEBU[0000] populate info failed, did not find tests/templates/info.yaml
PASS: tests/templates/deliverable/regular-template
PASS: tests/templates/deliverable/ytt-preprocess
PASS: tests/templates/deliverable/ytt-template YTT Template Test
FAIL: tests/templates/kpack
Name: image-template
Description: template requiring 'source' input
Error: expected does not equal actual: (-expected +actual):
map[string]any{
"apiVersion": string("kpack.io/v1alpha2"),
"kind": string("Image"),
- "metadata": map[string]any{"name": string("my-workload-name")},
+ "metadata": map[string]any{"name": string("another-identifier")},
"spec": map[string]any{
"builder": map[string]any{"kind": string("ClusterBuilder"), "name": string("go-builder")},
"serviceAccountName": string("cartographer-example-registry-creds-sa"),
"source": map[string]any{"blob": map[string]any{"url": string("some-passed-on-url")}},
"tag": strings.Join({
"some-default-prefix-",
- "my-workload-name",
+ "another-identifier",
}, ""),
},
}
We can see much more information about the failing test: the name, description and the actual error. We can see how
changing the name of the workload has changed the output of Cartographer from the expected state. For example, the
spec.tag
field was expected to be a join of some-default-prefix-
and my-workload-name
but was instead a join of
some-default-prefix-
and another-identifier
.
Let’s set the workload back to its original state:
git checkout ./tests/templates/kpack/workload.yaml
Reference
There are 7 types of information with which a cartotest may be configured. Those with an asterix (*) are required.
- Template *: The template under test
- Workload *: The workload that will pair with the supply chain/template
- Expected *: The expected object that will be created by Cartographer
- Supply Chain Inputs: The sources/images/configs assumed to have been created earlier in a supply chain
- Blueprint Params: The params specified in the supply chain
- YTT Preprocessing File: A file of ytt data values. Applied to the template before processing with Cartographer.
- Ignored Metadata Fields: Fields of the metadata that of the expected object that should not be tested.
Template
The template file may be specified in the following order of precedence:
- File named in the
.template
field ofinfo.yaml
- File named
template.yaml
- The template file inherited from parent directory
Workload
The workload file may be specified in the following order of precedence:
- File named in the
.workload
field ofinfo.yaml
- File named
workload.yaml
- The workload file inherited from parent directory
Expected
The expected file may be specified in the following order of precedence:
- File named in the
.expected
field ofinfo.yaml
- File named
expected.yaml
- The expected file inherited from parent directory
Supply Chain Inputs
The inputs may be specified in the .supplyChainInputs
field of info.yaml
. Otherwise, they are inherited from the
parent directory.
Blueprint Params
The inputs may be specified in the .blueprintParams
field of info.yaml
. Otherwise, they are inherited from the
parent directory.
YTT Preprocessing File
The workload file may be specified in the .ytt
field of info.yaml
Otherwise, they are inherited from the parent
directory.
Ignored Metadata Fields
- To ignore the entire metadata field, in
info.yaml
specifyignoreMetadata: true
- To ignore the metadata.ignoreOwnerRefs field, in
info.yaml
specifyignoreOwnerRefs: true
- To ignore the metadata.ignoreLabels field, in
info.yaml
specifyignoreLabels: true
- To ignore other metadata fields, in
info.yaml
add the field name toignoreMetadataFields
info.yaml Structure
Each folder of cartotests should contain an info.yaml
file which can specify test metadata, behavior and inputs.
# Name of the test
name: <string>
# Description of the test
description: <string>
# Path to the template file
template: <string>
# Path to the workload file
workload: <string>
# Path to the expected file
expected: <string>
# Path to the ytt preprocessing file
ytt: <string>
# Input values as if output from earlier steps of a supply chain
supplyChainInputs:
sources:
# string value should be the same as the name value
<string>:
name: <string>
url: <string>
revision: <string>
images:
# string value should be the same as the name value
<string>:
name: <string>
image: <string>
configs:
# string value should be the same as the name value
<string>:
name: <string>
config: <string>
# Parameters specified in a supply chain
blueprintParams:
- # Name of the parameter.
# Should match a template parameter name.
name: <string>
# Value of the parameter.
# If specified, workload properties are ignored.
# +optional
value: <any>
# DefaultValue of the parameter.
# Causes the parameter to be optional; If the workload does not specify
# this parameter, this value is used.
default: <any>
# If true, only this and other focused tests will run
focus: <bool>
# If true, test comparison will ignore all fields of metadata
ignoreMetadata: <bool>
# If true, test comparison will ignore all fields of metadata.ownerRefs
ignoreOwnerRefs: <bool>
# If true, test comparison will ignore all fields of metadata.labels
ignoreLabels: <bool>
# Test comparison will ignore all named fields of metadata
ignoreMetadataFields: [<string>]
How-Tos
How to Focus Tests
The output of failing tests can get long, particularly if you have many tests. It can be helpful to focus on one or a
few tests at a time. We’ll use the cartographer template tests and focus on just the kpack test. We do this by putting
focus = true
in the info.yaml
file.
yq '.focus = true' ./tests/templates/kpack/info.yaml -i
cartotest templates --directory ./tests/templates/
PASS: tests/templates/kpack
test suite failed due to focused test, check individual test case status
FAIL
We only see the output for the focused test.
We also see that while the individual test passed, cartotest
failed the test suite overall. This protects your tests
in CI, users will not get false positives if they forget to unfocus tests. Only when no test has been focused on will
cartotest
announce an overall PASS
and return a 0 exit code.
Let’s put the files back as they were:
git checkout .
How to Nest Tests and Inherit Parent State
Cartotests may be written in a nested fashion. Parent directories may declare some of the test state. Child directories
inherit this state (and may overwrite it). We can see this in action in the cartographer repo, looking at
./tests/templates/deliverable
. That folder has the common-workload.yaml
and common-expectations.yaml
files. In
info.yaml
we can see that they are set as the workload and expected file for the test. Then there are three
subfolders. Note that when the tests have been run, it has been these subfolders that were listed:
PASS: tests/templates/deliverable/regular-template
PASS: tests/templates/deliverable/ytt-preprocess
PASS: tests/templates/deliverable/ytt-template
If a folder has a child directory, cartotest assumes that the parent folder does not contain a complete test; e.g. only
leaf directories are treated as ready to test. We can observe this behavior by creating two empty subdirectories of
tests/templates/deliverable/regular-template
and running the tests.
mkdir tests/templates/deliverable/regular-template/some-dir
mkdir tests/templates/deliverable/regular-template/another-dir
cartotest templates --directory ./tests/templates/
PASS: tests/templates/deliverable/regular-template/another-dir
PASS: tests/templates/deliverable/regular-template/some-dir
PASS: tests/templates/deliverable/ytt-preprocess
PASS: tests/templates/deliverable/ytt-template
PASS: tests/templates/kpack
PASS
The child (leaf) directories are tested and not the parent.
Let’s set these back:
rm -rf tests/templates/deliverable/regular-template/some-dir
rm -rf tests/templates/deliverable/regular-template/another-dir
To observe inheritance of test state we can alter tests/templates/deliverable/common-expectation.yaml
and run the
tests.
yq '.spec.source.git.url = "https://github.com/ossu/computer-science/"' ./tests/templates/deliverable/common-expectation.yaml -i
cartotest templates --directory ./tests/templates/
PASS: tests/templates/kpack
FAIL: tests/templates/deliverable/regular-template
FAIL: tests/templates/deliverable/ytt-preprocess
FAIL: tests/templates/deliverable/ytt-template
FAIL
All of the child deliverable tests have failed. Let’s make a subset of tests pass by overwriting the expectation in a
child of deliverable. We’ll create a new expectation file in ./tests/templates/deliverable/regular-template
with the
proper values. Note that the filename expected.yaml
is special and is recognized by cartotest as an expectation file.
If another filename is used, it must be declared in the info.yaml
field expected
(as we can see in
tests/templates/deliverable/info.yaml
)
cp ./tests/templates/deliverable/common-expectation.yaml ./tests/templates/deliverable/regular-template/expected.yaml
yq '.spec.source.git.url = "https://github.com/vmware-tanzu/cartographer/"' ./tests/templates/deliverable/regular-template/expected.yaml -i
cartotest templates --directory ./tests/templates
PASS: tests/templates/kpack
PASS: tests/templates/deliverable/regular-template
FAIL: tests/templates/deliverable/ytt-template
FAIL: tests/templates/deliverable/ytt-preprocess
FAIL
The regular-template
test now passes while the other child folders of deliverable still fail.
Let’s clean up our directory:
git checkout .
rm tests/templates/deliverable/regular-template/expected.yaml