Acceptance tests are defined used Cucumber in Gherkin syntax, the steps are implemented in Go with the help of Godog.
Feature files written in Gherkin are kept in the features directory.
Entry point for the tests is the acceptance_test.go, which uses the established Go test to launch Godog.
To run the acceptance tests either run:
$ E2E_INSTRUMENTATION=true make acceptance
from the root of the repository.
Or move into the acceptance module:
$ cd acceptance
and run the acceptance_test.go test using go test:
$ go test ./...
The latter is useful for specifying additional arguments. Currently, the following are supported:
-persistif specified the test environment will persist after test execution, making it easy to recreate test failures and debug theeccommand line or acceptance test code-restorerun the tests against the persisted environment with-persist-no-colorsdisable colored output, useful when running in a terminal that doesn't support color escape sequences-tags=...comma separated tags to run, e.g.@bugs- to run only the scenarios tagged with@bugs, or@bugs,~@wipto run all scenarios that are tagged with@bugsbut not with@wip
These arguments need to be prefixed with -args parameter, for example:
$ go test ./acceptance -args -persist -tags=@focus
The -tags argument is for selecting acceptance test scenarios.
Also notice that there are different ways of specifying the path to the
acceptance tests. ./... can only be be used if -args is NOT used. Use,
./acceptance or github.com/conforma/cli/acceptance
in such cases.
Depending on your setup Testcontainer's ryuk container might need to be run as privileged container. For that, $HOME/.testcontainers.properties needs to be created with:
ryuk.container.privileged=true
Also, you will need to be able to run Docker without elevating the privileges, that is as a non-root user. See the documentation on Docker website how to accomplish that.
The acceptance tests execute the ec binary during test execution. (For this
reason E2E_INSTRUMENTATION=true make acceptance builds the binary prior to running the tests.)
To use a debugger, like delve, you must
determine what part of the code is being debugged. If it's part of the
acceptance module, github.com/conforma/cli/acceptance, or
it is a dependency of the acceptance module, then the debugger can be invoked
directly. However, if the code to be debugged is in any other module, first
run the acceptance tests in -persist mode. The, scan the test logs for the
exact command used during the test you want to debug. Finally, run the ec code
via the debugger, for example:
dlv debug -- track bundle --bundle localhost:49167/acceptance/bundle:tag
You can use the existing step implementations and add scenarios to existing or new feature files. For any new steps Godog will print the function snippet that needs to be implemented to support it. Please take care to refactor and extend the functionality of existing step implementations and add new steps only when existing steps are not sufficient. Keeping the acceptance test code well organized and succinct/simple as it can be is the key to long term viability and maintainablity of the acceptance tests.
Don't introduce dependencies on services not stubbed by the test environment.
We use Testcontainers extensively to make
the test environment self-contained and deterministic. When using
Testcontainers, pass the testcontainers.ContainerRequest through
testenv.TestContainersRequest to make sure the persist flag is honored.
We use WireMock to stub HTTP APIs, such as Kuberetes apiserver or Rekord.
Make sure not to introduce global state as this makes it difficult to run tests
concurrently. Running the acceptance tests concurrently is important for fast
feedback/turnaround. All state should be held in context.Context's values.
Make sure that keys for those values are unique and package-private to prevent
dependencies from client packages on the context.Context state specific to
the implementation of the stub/helper.
When possible use the exact types (structs) for the API payloads and (un)marshall to JSON/YAML at the edges of the stub. This helps with making the payloads future-proof.
Pay attention to the testenv.Persisted() if the test is running whe the
persisted flag when cleaning up state that is needed to reproduce the test
failure.
Whenever possible include enough information in the test failure to make it easy to reason about the context of the failure. Consider situations where all that is available is the log output of the tests and the error message from the test failure.
We use
github.com/gkampitakis/go-snaps for
snapshotting mostly for asserting the standard error and output of the CLI with
the the output should match the snapshot step. The snapshots are placed in the
features/__snapshots__ directory.
Sometimes the output changes and the snapshots need updating, this can be done
by setting UPDATE_SNAPS=true environment variable.
NOTE: If subset of features is run, there might be a message from the snappshoting library that there are outdated snapshots, the cause of this might be that the scenario generating the snapshot was not run.
context deadline exceeded: failed to start container may occur in some
cases. sudo systemctl restart docker usually fixes it.
Running on MacOS has been tested using podman machine. Listed below are the recommended podman machine settings.
- Set rootful to true
podman machine set --rootful=true
- Set memory to 4GB
podman machine set -m 4096
- Set cpus to 2
podman machine set --cpus 2
- Disable selinux on the podman vm
podman machine sshvi /etc/selinux/config- Set
SELINUX=disable
- Rename the default bridge in containers.conf
podman machine sshvi /etc/containers/containers.conf- Set
[network] default_network="bridge"
- Restart the vm
podman machine stoppodman machine start