skip to content

Show how you'd use ProjectBuilder to assert that your plugin registered a task, added an extension, and created a configuration.

level: middleimportance: must knowfreq 45%

answer

  1. build → apply → assert
  2. tasks.findByName / extensions.findByType / configurations.findByName
  3. findByName null vs getByName throws
  4. register() realized on query
  5. Property.get() reads defaults

basics

~10 s

Build a Project, apply the plugin, then assert: project.tasks.findByName("x"), project.extensions.findByName("y"), and project.configurations.findByName("z") are non-null. Each check confirms a piece of configuration-phase wiring.

solid answer

~30 s

After `val project = ProjectBuilder.builder().build()` and `project.plugins.apply(MyPlugin::class.java)`, you assert against the project's containers. Use `findByName(...)` (returns null when absent) rather than `getByName(...)` (throws) when you want clean assertions. For a **task**: `assertNotNull(project.tasks.findByName("deploy"))`, and you can cast/check its type or properties. For an **extension**: `val ext = project.extensions.findByType(MyExtension::class.java); assertNotNull(ext); assertEquals("default", ext!!.value.get())` — extensions registered via `extensions.create` expose their `Property` defaults at configuration time. For a **configuration**: `assertNotNull(project.configurations.findByName("myTool"))`, and you can check `isCanBeResolved`/`isCanBeConsumed`. Because the lazy task/`Property` APIs are realized at configuration time, these assertions verify exactly the wiring your plugin's `apply()` performed, without running the build.

code

kotlin · 18 lines
kotlin
@Test
fun `plugin wires task, extension, and configuration`() {
    val project = ProjectBuilder.builder().build()
    project.plugins.apply(MyPlugin::class.java)

    // task
    assertNotNull(project.tasks.findByName("deploy"))

    // extension + its default
    val ext = project.extensions.findByType(GreetingExtension::class.java)
    assertNotNull(ext)
    assertEquals("Hello", ext!!.message.get())

    // configuration role
    val conf = project.configurations.findByName("myTool")
    assertNotNull(conf)
    assertTrue(conf!!.isCanBeResolved)
}

go deeper

for a junior

Demonstrate the build→apply→assert pattern with findByName for a task and an extension.

for a middle

Show all three container types, prefer findByName, and read Property defaults via get(); know register realizes on query.

for a senior

Discuss asserting configuration roles (resolvable/consumable) and configured Property values, and why these stay in the configuration phase.

for a principal

Define a reusable test helper/convention for plugin wiring assertions so teams write consistent, fast coverage across many plugins.

## The pattern: build → apply → assert Every ProjectBuilder wiring test follows three steps. ```kotlin val project = ProjectBuilder.builder().build() // 1. build project.plugins.apply(MyPlugin::class.java) // 2. apply // 3. assert against project's containers ``` ## Asserting a task was registered `project.tasks` is a `TaskContainer`. Prefer `findByName` (nullable, no throw) for assertions: ```kotlin assertNotNull(project.tasks.findByName("deploy")) val task = project.tasks.findByName("deploy") as MyDeployTask assertEquals("prod", task.environment.get()) ``` Note: if your plugin uses the lazy `tasks.register("deploy")`, the task provider isn't *realized* until something queries it. Calling `findByName` (or `getByName`/`named(...).get()`) forces realization, so the registration action runs and you can inspect the configured object. ## Asserting an extension was added Extensions added via `project.extensions.create("greeting", GreetingExtension::class.java)` are available immediately: ```kotlin val ext = project.extensions.findByType(GreetingExtension::class.java) assertNotNull(ext) assertEquals("Hello", ext!!.message.get()) // Property default ``` `findByType` resolves by class; `findByName` resolves by the registered name. Both are valid. ## Asserting a configuration was created `project.configurations` is the `ConfigurationContainer`. A plugin commonly creates and shapes a configuration's role: ```kotlin val conf = project.configurations.findByName("myTool") assertNotNull(conf) assertTrue(conf!!.isCanBeResolved) assertFalse(conf.isCanBeConsumed) ``` In modern Gradle you'd typically create roles via `configurations.resolvable("myTool")` or `configurations.consumable("myToolElements")`; ProjectBuilder lets you assert the resulting `isCanBeResolved`/`isCanBeConsumed` flags. ## findByName vs getByName in tests - `findByName("x")` → returns `null` if missing. Best for `assertNotNull` style — the failure message is your assertion, not an exception. - `getByName("x")` → throws `UnknownDomainObjectException` if missing. Fine, but the test fails with a less intentional message. ## Why this is reliable All three checks observe **configuration-phase** state — exactly the work your `apply()` did. No execution phase is needed, so the tests stay fast and deterministic.

  • Why prefer findByName over getByName in a ProjectBuilder assertion?
    findByName returns null when the object is missing, so `assertNotNull` produces a clear assertion failure. getByName throws UnknownDomainObjectException, which surfaces as a less intentional test error.
  • If your plugin registers a task lazily with tasks.register, does it exist for the test to find?
    Yes — registration creates a provider, and querying it (findByName/named().get()) realizes the task, running its configuration action so you can inspect it. The registration itself is enough; you just have to touch it.
  • How do you assert an extension's default value was set correctly?
    Resolve the extension via findByType, then read its Property with .get(), e.g. ext.message.get(), since extension defaults are applied during apply() at configuration time.

saying these in an interview costs you the question

  • Using getByName everywhere and getting confusing exception-based failures.
  • Trying to read a task's runtime output (which requires execution) instead of its configured properties.
  • Asserting task ordering/dependencies as if they executed — only the wiring is present, not execution results.

context