Skip to content

[Docs] WorkflowTestCase silently connects to prod Temporal when TEMPORAL_ADDRESS is already set in container env (PHPUnit <env> doesn't override by default) #744

@ilyazastrognov

Description

@ilyazastrognov

What are you really trying to do?

Run the time-skipping test suite from inside a Docker container that already has TEMPORAL_ADDRESS=temporal:7233 baked into the
runtime environment (because the same container image runs both the prod app and the tests).

Describe the bug

The official testing-suite docs (https://docs.temporal.io/develop/php/testing-suite) recommend setting TEMPORAL_ADDRESS in
phpunit.xml:

<env name="TEMPORAL_ADDRESS" value="127.0.0.1:7233"/>

This silently does nothing when the variable is already set in the OS environment. PHPUnit's directive is non-overriding by
default — it only sets the value if the var doesn't exist. The fix in PHPUnit is force="true":

<env name="TEMPORAL_ADDRESS" value="127.0.0.1:7233" force="true"/>

But even force="true" only writes to $_ENV / $_SERVER, not to the OS env that \getenv() reads. Quoting the PHP manual:

_ getenv() retrieves environment variables. By default, it queries the OS environment, not $ENV.

Temporal\Testing\WorkflowTestCase::setUp() calls \getenv('TEMPORAL_ADDRESS'):

  $temporalAddress = \getenv('TEMPORAL_ADDRESS') ?: '127.0.0.1:7233';              
  $this->workflowClient = new WorkflowClient(ServiceClient::create($temporalAddress)); 

So in any environment where TEMPORAL_ADDRESS is already set in OS env (CI runners, Docker containers built from the prod image,
hosted dev environments) — the test client connects to whatever production-style address is baked in, not the in-process test
server.

Symptom

Every workflow start times out at withWorkflowExecutionTimeout with WorkflowFailedException retryState=3. The error message gives no
hint about address mismatch — users assume their workflow logic is broken, the worker isn't registered, or task-queue routing is
wrong. Debugging takes hours.

Diagnostic that would have saved us hours

While a hung test is running, in another shell:
temporal workflow show --address 127.0.0.1:7233 -w <workflowId-from-failure>
"Execution not found in mutable state"

Means the client wrote to a different server. There is currently no log line, no warning, no health-check from WorkflowTestCase that
would surface this.

Workaround

In the test bootstrap, before any SDK code loads:

  \putenv('TEMPORAL_ADDRESS=127.0.0.1:7233');                                                                                         
  $_ENV['TEMPORAL_ADDRESS'] = '127.0.0.1:7233';                                    
  $_SERVER['TEMPORAL_ADDRESS'] = '127.0.0.1:7233';    

All three channels are needed because different SDK code paths read different sources.

Suggested Fixes (in order of preference)

  1. Auto-detect and warn on mismatch. When WorkflowTestCase::setUp() resolves TEMPORAL_ADDRESS, if a test server is running on
    127.0.0.1 but getenv() points elsewhere, log a loud warning. Or: if Environment::startTemporalTestServer() was just called by the
    bootstrap, force the client to that address.
  2. Document the gotcha. Add to the testing-suite docs: a section called "Running tests in containers where TEMPORAL_ADDRESS is
    pre-set" with the three-channel putenv workaround. Right now docs assume a clean shell environment.
  3. Have WorkflowTestCase accept an explicit address. Allow protected string $testServerAddress = '127.0.0.1:7233'; override on the
    test class, bypassing env resolution entirely. Same shape as Java's TestWorkflowEnvironment.newInstance().

Versions

  • temporal/sdk 2.17
  • PHP 8.4
  • PHPUnit 10.x
  • Linux, x86_64
  • Reproduced inside Docker (image inherits TEMPORAL_ADDRESS=temporal:7233 from docker-compose.yml).

Related

This is an instance of a broader story — testing-suite docs assume a "clean room" environment. Combined with LINK, the whole
WorkflowTestCase user experience needs a documentation pass for production-realistic setups.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions