skip to content

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.

level: middleimportance: should knowfreq 35%

answer

  1. systemProperties.putAll(map)
  2. providers.gradleProperty / providers.environmentVariable
  3. getOrElse default
  4. config-cache forbids project.property at exec time
  5. lazy Provider API

basics

~10 s

Use 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 s

To 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 lines
kotlin
tasks.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

for a junior

Know you can set many at once via the systemProperties map.

for a middle

Bridge -P/env values with the Provider API and supply defaults via getOrElse.

for a senior

Explain configuration-cache constraints that make providers the correct choice over project state reads.

for a principal

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)

context