Show how to forward several system properties at once into tests and how to bridge values from Gradle properties or the command line in a configuration-cache-friendly way.
answer
- systemProperties.putAll(map)
- providers.gradleProperty / providers.environmentVariable
- getOrElse default
- config-cache forbids project.property at exec time
- lazy Provider API
basics
~10 sUse systemProperties = mapOf(...) or putAll to set many at once. To bridge a -P value safely, read it via providers.gradleProperty("x") (a Provider) rather than project.property at execution time, keeping it configuration-cache compatible.
solid answer
~30 sTo set many properties, assign or merge into the `systemProperties` map: `systemProperties.putAll(mapOf("a" to "1", "b" to "2"))`. To bridge external input (a `-P` Gradle property or an env var) into the test, read it through the **Provider API** — `providers.gradleProperty("appEnv")` or `providers.environmentVariable("APP_ENV")` — and forward the resolved value with `systemProperty`/`environment`. Using providers (rather than `project.property(...)` evaluated during task execution) keeps the build **configuration-cache compatible**, because providers are lazy and the configuration cache forbids reading mutable `project` state at execution time. A defaulting pattern like `providers.gradleProperty("appEnv").getOrElse("local")` gives a sane fallback. This makes test config explicit, batchable, and CI-overridable.
code
kotlin · 7 linestasks.test {
systemProperties.putAll(
mapOf("feature.x" to "on", "app.locale" to "en"),
)
systemProperty("app.env", providers.gradleProperty("appEnv").getOrElse("local"))
environment("TOKEN", providers.environmentVariable("CI_TOKEN").getOrElse(""))
}go deeper
Know you can set many at once via the systemProperties map.
Bridge -P/env values with the Provider API and supply defaults via getOrElse.
Explain configuration-cache constraints that make providers the correct choice over project state reads.
Encapsulate the bridging pattern in a convention plugin so all modules forward CI config consistently and cache-safely.
## Setting many properties at once `systemProperties` is a mutable `Map<String, Any>`. You can replace it or merge: ```kotlin tasks.test { systemProperties.putAll( mapOf( "app.env" to "ci", "feature.x" to "on", "junit.jupiter.execution.parallel.enabled" to "true", ), ) } ``` The same exists conceptually for `environment` (its own map) for env vars. ## Bridging command-line / Gradle properties You often want CI to override a value: `./gradlew test -PappEnv=staging`. Read it lazily through the Provider API and forward it: ```kotlin tasks.test { val appEnv = providers.gradleProperty("appEnv").getOrElse("local") systemProperty("app.env", appEnv) } ``` `providers.gradleProperty(name)` returns a `Provider<String>` resolved from `-P`, `gradle.properties`, etc. `providers.environmentVariable(name)` does the same for an OS env var. Both are **lazy** and **side-effect free**. ## Why providers, not project.property? With the **configuration cache** enabled, Gradle forbids reading mutable `Project` state (like `project.findProperty(...)`) at **execution time**, because the configuration is serialized and replayed. The Provider API is the supported, lazy way to obtain external values without tripping the configuration cache. So: ```kotlin // Configuration-cache safe systemProperty("app.env", providers.gradleProperty("appEnv").getOrElse("local")) // Risky: reads project state, can break with config cache if done at exec time // systemProperty("app.env", project.findProperty("appEnv") ?: "local") ``` Reading the provider during **configuration** (as above) resolves a concrete String you hand to `systemProperty`, which is fine and cache-friendly. ## Putting it together This pattern — bulk-set defaults with a map, override individual keys from providers — gives you a clean, testable, CI-overridable test configuration that plays well with both the build cache and the configuration cache.
- Why prefer providers.gradleProperty over project.findProperty when forwarding to systemProperty?providers.gradleProperty is lazy and configuration-cache compatible; reading mutable project state at execution time can break the configuration cache.
- How do you give a forwarded property a default when the -P flag is absent?Use .getOrElse("default") on the Provider, e.g. providers.gradleProperty("appEnv").getOrElse("local").
- What's the difference between providers.gradleProperty and providers.environmentVariable?The first resolves a Gradle project property (-P / gradle.properties); the second resolves an OS environment variable. Both return lazy Providers.
saying these in an interview costs you the question
- Reading project.findProperty at execution time and assuming it's configuration-cache safe
- Forgetting a default, so a missing -P yields null and a NPE in tests
- Confusing gradleProperty (-P) with a system property (-D)