You pass -Ddb.url=... on the command line but your test fails to read it. Why, and how do you make it visible to the tests?
answer
- daemon JVM vs forked worker JVM
- -D not inherited across fork
- tasks.test { systemProperty(...) } to forward
- -P project props never reach workers directly
- providers.* keeps inputs tracked
basics
~10 sTests run in a forked JVM, so the daemon's -D system property isn't inherited. Propagate it explicitly: tasks.test { systemProperty("db.url", providers.systemProperty("db.url").get()) }.
solid answer
~40 s`-Ddb.url=...` sets a system property on the **Gradle daemon JVM**. But `Test`, `JavaExec`, and other forking tasks spawn a **separate worker JVM**, and system properties are not inherited across that boundary. So `System.getProperty("db.url")` inside the test returns `null`. To fix it, forward the value onto the forking task: ```kotlin tasks.test { systemProperty("db.url", providers.systemProperty("db.url").getOrElse("jdbc:h2:mem:test")) } ``` Use `providers.systemProperty(...)` rather than `System.getProperty(...)` so the value is declared as a tracked input for the configuration cache and up-to-date checks. The same applies to `-P` project properties: read them in the script with `providers.gradleProperty(...)` and forward them onto the worker JVM via `systemProperty(...)` (since the worker has no notion of Gradle project properties at all).
code
kotlin · 6 linestasks.test {
// Forward selected daemon system properties into the test worker
systemProperty("db.url", providers.systemProperty("db.url").getOrElse("jdbc:h2:mem:test"))
// Surface a -P project property to test code as a system property
systemProperty("app.env", providers.gradleProperty("appEnv").getOrElse("local"))
}go deeper
Recognize that tests fork a separate JVM and need the property forwarded with systemProperty.
Show the tasks.test { systemProperty(...) } fix and explain the daemon-vs-worker boundary clearly.
Use providers.* for cache-tracked inputs and discuss why project properties never cross the fork.
Establish a pattern: narrowly forward named properties (not the whole property set) to keep workers hermetic and builds cacheable across CI.
## The forked-JVM boundary Gradle's daemon is one JVM. Tasks like `Test`, `JavaExec`, `JavaCompile` (with forking), and custom `WorkerExecutor` actions often run in **separate worker JVMs** for isolation. System properties set on the daemon — whether via `-D` on the CLI or `systemProp.` in `gradle.properties` — live only on the daemon and are **not** inherited by those workers. ### Symptom ```bash gradle test -Ddb.url=jdbc:postgresql://ci/app ``` Inside the test: `System.getProperty("db.url")` is `null`, because the test worker is a fresh JVM. ### Fix: explicit propagation The forking task exposes a `systemProperty(name, value)` / `systemProperties(map)` API that injects properties into the worker: ```kotlin tasks.test { // Forward a -D value through to the worker systemProperty("db.url", providers.systemProperty("db.url").getOrElse("jdbc:h2:mem:test")) // Or forward a -P project property systemProperty("app.env", providers.gradleProperty("appEnv").getOrElse("local")) } ``` ### Project properties never cross the boundary on their own A worker JVM has no `Project` object, so `-P` values are *never* directly visible there. The only way to surface a project property in worker code is to convert it into something the worker understands — a system property (`systemProperty(...)`), an environment variable (`environment(...)`), or a generated resource/argument. ### Why use the providers API Reading via `providers.systemProperty`/`providers.gradleProperty` and forwarding the provider declares the value as a build input. That keeps up-to-date checks and the configuration cache correct: change `-Ddb.url` and the test task re-runs; eager `System.getProperty` reads risk being treated as undeclared inputs. ### Bulk forwarding (use sparingly) You can forward many at once, but forwarding *all* daemon system properties couples the worker to ambient state and hurts cacheability: ```kotlin tasks.test { systemProperties(System.getProperties().mapKeys { it.key.toString() }) } // discouraged ``` Prefer naming exactly the properties the tests need.
- Why can't a forked test read a -P project property at all without help?Worker JVMs have no Gradle Project object; project properties are a build-model concept. You must translate it into a system property, env var, or generated input.
- What is the downside of forwarding all System.getProperties() to the test worker?It couples tests to ambient daemon state, leaks unrelated properties, and harms up-to-date/configuration-cache correctness. Forward only what the tests need.
- How would you instead pass the value as an environment variable?Use tasks.test { environment("DB_URL", provider) } and read System.getenv("DB_URL") in the test.
saying these in an interview costs you the question
- Assuming -D on the daemon is automatically visible to test workers.
- Trying to read a -P project property directly inside test code via project.property.