Skip to content

Test Session Semantics

The test session semantics is a feature that allows Testcontainers for Go to identify the current test session and tag the containers created by the library with a unique session ID.

This is needed because each Go package will be run in a separate process, so we need a way to identify the current test execution to aggregate the tests executed in it.

By test session, we mean:

  • a single go test invocation (including flags).
  • a single go test ./... invocation, for all subpackages from that location (including flags).
  • the execution of a single test or a set of tests using the IDE.

As a consequence, Testcontainers for Go will use the parent process ID (pid) of the current process and its creation date to generate a unique session ID.

We are using the parent pid because the current go test process running a given Go package will be a child of one of the following:

  • the process that is running the tests, e.g.: go test;
  • the process that is running the application in development mode, e.g. go run main.go -tags dev;
  • the process that is running the tests in the IDE, e.g.: go test ./....

That's why we need to use the parent pid to identify the current test session, as it must be unique.

Finally, we will hash the combination of the testcontainers-go: string with the parent pid and the creation date of that parent process to generate a unique session ID.

After that, the sessionID will be used to:

  • identify the test session, aggregating the test execution of multiple packages in the same test session.
  • pass the sessionID to the container runtime, as an HTTP header to the daemon.
  • tag the containers created by Testcontainers for Go, adding a label to the container with this session ID.

Overriding the session ID

Deriving the session ID from the parent process works when the processes taking part in a test session share a parent, which is the case for go test and for the IDE. When that is not true, the processes end up in different sessions: some build systems and CI setups run each test target in isolation, and the parent process they are given is not guaranteed to be stable or shared.

For those cases the session ID can be set explicitly, either with the TESTCONTAINERS_SESSION_ID environment variable or with the session.id property. When set, the value is used as-is, instead of being derived, so every process configured with the same value takes part in the same session.

Warning

Setting this value will preclude runs from creating more than one reaper. Therefore, changes to Ryuk settings past its creation will be ignored.

The session ID is used to build the name of the reaper container (reaper_<sessionID>), so a configured value must produce a name the container runtime accepts. It must:

  • not be empty;
  • contain only alphanumeric characters, dots (.), hyphens (-) and underscores (_);
  • be at most 121 characters long, so that the resulting container name stays within the 128 character limit imposed by the container runtime.

A configured value that does not satisfy these constraints is rejected when the configuration is read, reporting the offending value and the reason. The session IDs generated by the library always satisfy them, so this only applies to values coming from the configuration.

Bazel

Bazel is a concrete example of a build system where the session ID needs to be set explicitly, because it does not provide the conditions the derivation relies on. Its Test Encyclopedia states that "the current process id, process group id, session id, and parent process id are unspecified", so the parent process the library would derive the session ID from is not guaranteed to be stable or shared across test targets. Sharding makes this more visible, as the test runner is launched once per shard.

Two more details of the test environment matter:

  • HOME is set to the value of $TEST_TMPDIR, a private directory for the test, so the ~/.testcontainers.properties file is not picked up.
  • The test environment is sanitised: tests "should not depend on the presence, absence, or value of any environment variable not listed" in the encyclopedia, so TESTCONTAINERS_SESSION_ID does not reach the test unless it is explicitly propagated.

That leaves the environment variable, declared with --test_env, as the way to share a session across test targets. The value can be taken from the invocation environment:

bazel test //... --test_env=TESTCONTAINERS_SESSION_ID

or set independently of it:

bazel test //... --test_env=TESTCONTAINERS_SESSION_ID=my-session