How to test a GradleScanner with project dependency references

1,182 views
Skip to first unread message

John Rodriguez

unread,
Nov 21, 2024, 9:24:20 PM11/21/24
to lint-dev
I've written a detector that errors when the legacy project dependency syntax is used in a Gradle build script, favoring the typesafe accessor syntax, i.e.,:

instead of
```
implementation project(":foo:bar")
```

use:
```
implementation projects.foo.bar
```

but I can't get the following test set up properly:

```
  @Test
  fun `legacy project path syntax detects error`() {
    lint()
      .files(
        gradle(
          """
          apply plugin: 'com.android.library'
          dependencies {
            implementation project(":foo:bar")
          }
          """
        ).indented()
      )
      .issues(GradleProjectPathDetector.LEGACY_PROJECT_PATH_SYNTAX)
      .run()
      .expect("") // left blank, so that a test failure will inform what to paste here
      .expectFixDiffs("") // ditto
  }
```

I tried adding a settings.gradle to declare 2 modules and an empty foo/bar/build.gradle to appease the test run, but that didn't work.

I also stepped through GradleModelMocker and its associated test, but looks like there's either no support for this at the moment, or I'm just missing something.

Tapiwa Mandere

unread,
Oct 6, 2025, 8:26:04 PM (8 days ago) Oct 6
to lint-dev

Lint's test infrastructure doesn't fully resolve Gradle project dependencies by default. The project(":foo:bar") call requires actual module resolution, which the test harness doesn't provide out of the box.

Solutions Option 1: Use allowCompilationErrors()

Since you're checking syntax, not actual resolution, you can allow compilation errors:

kotlin

@Test

fun `legacy project path syntax detects error`() {

    lint()

        .files(

            gradle(

                """

                apply plugin: 'com.android.library'

                dependencies {

                  implementation project(":foo:bar")

                }

                """

            ).indented()

        )

        .issues(GradleProjectPathDetector.LEGACY_PROJECT_PATH_SYNTAX)

        .allowCompilationErrors() // Add this

        .run()

        .expect("")

        .expectFixDiffs("")

}

Option 2: Mock the Project Structure

If you need actual project resolution, create a multi-module setup:

kotlin

@Test

fun `legacy project path syntax detects error`() {

    lint()

        .files(

            gradle(

                """

                apply plugin: 'com.android.library'

                dependencies {

                  implementation project(":foo:bar")

                }

                """

            ).indented(),

            // Add settings.gradle

            gradle(

                """

                include ':foo:bar'

                """

            ).name("settings.gradle"),

            // Add the subproject's build file

            gradle(

                """

                apply plugin: 'com.android.library'

                """

            ).path("foo/bar/build.gradle")

        )

        .issues(GradleProjectPathDetector.LEGACY_PROJECT_PATH_SYNTAX)

        .run()

        .expect("")

        .expectFixDiffs("")

}

Option 3: Use PSI-based Detection (Recommended for Syntax Checks)

Since you're detecting syntax patterns rather than resolved dependencies, you likely don't need full Gradle resolution. Your detector should work on the raw AST/PSI:

kotlin

class GradleProjectPathDetector : Detector(), GradleScanner {

    override fun checkMethodCall(

        context: GradleContext,

        statement: Call,

        parent: UElement,

        resolved: PsiMethod

    ) {

        if (statement.methodName == "project" &&

            statement.valueArguments.size == 1) {

            // Report the issue

            context.report(

                LEGACY_PROJECT_PATH_SYNTAX,

                statement,

                context.getLocation(statement),

                "Use type-safe project accessor instead of project(\":path\")"

            )

        }

    }

}

With this approach, Option 1 (allowCompilationErrors()) should be sufficient.

Option 4: Check the Actual Error Message

Run your test as-is and see what error you get. Sometimes the test infrastructure provides helpful hints about what's missing. Remove the empty .expect("") temporarily to see what lint actually reports.

Most Likely Solution

For a syntax-based detector, use Option 1 with allowCompilationErrors(). The Gradle test infrastructure doesn't need to resolve dependencies for you to detect the pattern project(":foo:bar") in the source.

If that doesn't work, share the error message you're getting and I can provide more specific guidance!

Reply all
Reply to author
Forward
0 new messages