How do you set environment variables for the test JVM in Gradle, and how does that differ from system properties?
answer
- environment(name, value)
- System.getenv vs System.getProperty
- env = OS process env, sysprop = -D flag
- inherited from daemon + overridden
- both are task inputs
basics
~10 sUse Test.environment("KEY", "value") or the environment map. Read it in tests with System.getenv("KEY"). Unlike systemProperty (a -D flag read via System.getProperty), this sets a real process environment variable.
solid answer
~40 sThe `Test` task exposes `environment(String, Object)` and an `environment` map to set **environment variables** on the forked test process. Inside the test you read them with `System.getenv("KEY")`. This differs from `systemProperty`: system properties become `-Dkey=value` JVM arguments read via `System.getProperty`, while environment variables are part of the OS process environment read via `System.getenv`. Use env vars when the code-under-test reads configuration the same way it would in production (many 12-factor apps read env vars), or when a tool/library only honors env. A subtlety: environment variables are inputs to the task; if you derive them from non-reproducible sources, you can break up-to-date checks. Prefer stable, declared values. Note you cannot fully delete inherited env vars via the DSL — you only add/override entries on top of the daemon's inherited environment.
code
kotlin · 5 linestasks.test {
systemProperty("app.flag", "on") // System.getProperty("app.flag")
environment("APP_ENV", "ci") // System.getenv("APP_ENV")
environment["DB_URL"] = "jdbc:h2:mem:test"
}go deeper
Know environment(...) sets an env var read with System.getenv, separate from systemProperty.
Articulate the sysprop-vs-env distinction (-D/getProperty vs process env/getenv) and choose based on the production read path.
Discuss inheritance from the daemon, that entries are task inputs, and how unstable values hurt caching.
Define org conventions for prod-parity config injection and secret handling in tests, avoiding leaking real credentials into build inputs.
## System property vs environment variable These are two different mechanisms, both available on the `Test` task: | | System property | Environment variable | |---|---|---| | Set with | `systemProperty(name, value)` | `environment(name, value)` | | Becomes | `-Dname=value` JVM arg | OS process env entry | | Read in test | `System.getProperty(name)` | `System.getenv(name)` | | Typical use | JVM/app config flags | 12-factor / prod-parity config | ## Setting environment variables ```kotlin tasks.test { environment("APP_ENV", "ci") environment["DB_URL"] = "jdbc:postgresql://localhost/test" } ``` The forked test JVM now sees `APP_ENV` and `DB_URL` via `System.getenv`. The fork **inherits** the daemon's environment, and these entries are layered on top (adding or overriding). ## Reading in tests ```kotlin val env = System.getenv("APP_ENV") ``` ## When to use which - Use **system properties** for things your app reads as `-D` flags or via `System.getProperty` (e.g. `app.env`). - Use **environment variables** when the production code reads `System.getenv` (common for cloud config, secrets injected by the platform, or libraries that only honor env). Matching the production read path makes the test realistic. ## Up-to-date / caching note Both the `environment` map and `systemProperties` map are **task inputs**. Gradle tracks them, so changing a value re-runs tests. The danger is wiring in values that change every run (timestamps, random ports, absolute paths). That makes the task perpetually out of date and can defeat the build cache. Keep declared values stable and reproducible. ## Limitation The DSL lets you add/override env entries but does not provide a clean way to start from an empty environment — the fork inherits the daemon's environment by default. If full isolation matters, you typically scrub specific keys or document the assumption rather than relying on a wipe.
- Your app reads config with System.getenv in production. Should your test set a systemProperty or environment? Why?environment, so the test exercises the same read path (System.getenv) as production. A systemProperty wouldn't be visible to System.getenv.
- Can you start the test JVM with a completely empty environment via the DSL?Not cleanly — the fork inherits the daemon's environment and you layer overrides on top. You can override/scrub specific keys but there's no first-class 'empty environment' switch.
- Are the environment entries tracked as task inputs?Yes. Changing them invalidates the up-to-date check and re-runs the tests, and unstable values can defeat the build cache.
saying these in an interview costs you the question
- Saying System.getProperty can read an environment variable set via environment()
- Treating systemProperty and environment as interchangeable
- Wiring volatile values into environment and then wondering why tests always re-run