I’m using version catalog feature in one of my project where I published my catalog to artifactory and in another project (lets say consumer project) I’m importing that catalog.
I have some test cases using ProjectBuilder and some with GradleRunner (Gradle Test Kit). When I run the ProjectBuilder test cases it is giving an error
org.gradle.api.UnknownDomainObjectException: Extension of type 'VersionCatalogsExtension' does not exist. Currently registered extension types: [ExtraPropertiesExtension, SpringBootExtension, StandardDependencyManagementExtension, ProjectOperations, AsciidoctorJExtension, OpenApiExtension, OpenApi3Extension, PostmanExtension, BasePluginExtension, DefaultArtifactPublicationSet, SourceSetContainer, ReportingExtension, JavaToolchainService, JavaPluginExtension, TestingExtension, ContractVerifierExtension]
at org.gradle.internal.extensibility.ExtensionsStorage.getHolderByType(ExtensionsStorage.java:89)
at org.gradle.internal.extensibility.ExtensionsStorage.getByType(ExtensionsStorage.java:74)
at org.gradle.internal.extensibility.DefaultConvention.getByType(DefaultConvention.java:172)
at org.gradle.internal.extensibility.DefaultConvention.getByType(DefaultConvention.java:167)
Here is how my custom precompiled plugin is :
CustomPlugin.kt
class CustomPlugin: Plugin<Project> {
override fun apply(project: Project) {
project.applyDependencies()
}
private fun Project.applyDependencies() {
val dependencyHandler = project.dependencies
val versionCatalog =
project.extensions.getByType(VersionCatalogsExtension::class.java).named("libs")
using dependencyHandler.add("implementation" , versionCatalog.findLibrary("springframework-cloud").get().get())
..... // some more dependencies
}
}
CustomPluginTest.kt
class CustomPluginTest {
@Test
fun testDependencies() {
val project = ProjectBuilder.builder().build()
}
}
When I tested using spock framework, I was getting the same exception and it is throwing near project.pluginManager.apply("example-plugin") we tried mock after this line.
Sure, of course you have to create the extension before you call the apply, as your plugin is trying to access the version catalog in its apply method.
That’s like accessing a field before assigning a value to it and then wondering why you get a NullPointerException.
I think I was able to solve this finally (hopefully ) . I’m using Junit5 only, so added mockito-kotlin dependency to keep my changes minimal. Spock I was not using, but first tried to see how to implement based on your input and then tried to find equivalent in mockito-kotlin.
@Test
fun testPlugin() {
val project = ProjectBuilder.builder().build()
val filePath = this::class.java.classLoader.getResource("version-catalog/libs.versions.toml").file
val versions = readVersionsFromTomlFile(filePath)
val libraries = readLibrariesFromTomlFile(filePath, versions)
val versionCatalog = createMockVersionCatalog(versions, project, libraries)
val versionCatalogsExtension = createMockVersionCatalogsExtension(versionCatalog, "libs")
project.extensions.add(VersionCatalogsExtension::class.java, "libs", versionCatalogsExtension)
project.pluginManager.apply("example-plugin")
val implementationDependencies = project.configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).dependencies
val hasSpringCloudDependency = implementationDependencies.any { dependency ->
dependency.group == "org.springframework.cloud" && dependency.name == "spring-cloud-dependencies"
}
assertEquals(true, hasSpringCloudDependency)
val assertPlugins = listOf("org.springframework.boot", "io.spring.dependency-management", "org.asciidoctor.jvm.convert", "com.epages.restdocs-api-spec", "org.springframework.cloud.contract")
assertPlugins.forEach { plugin ->
assertEquals(true, project.pluginManager.hasPlugin(plugin))
}
}
Here are the helper method
private fun createMockVersionCatalog(versions: Map<String, String>, project: Project, libraries: Map<String, MinimalExternalModuleDependency>): VersionCatalog = mock<VersionCatalog> {
on { findVersion(any()) }.thenAnswer { invocation ->
val requestedVersionName = invocation.arguments[0] as String
val version = versions[requestedVersionName]
Optional.ofNullable(version?.let {
mock<VersionConstraint> { on { requiredVersion }.thenReturn(version) }
})
}
on { findLibrary(any()) }.thenAnswer { invocation ->
val requestedLibraryName = invocation.arguments[0] as String
libraries[requestedLibraryName]?.let { dependency -> Optional.of(project.provider { dependency }) }
}
}
private fun createMockVersionCatalogsExtension(versionCatalog: VersionCatalog, name: String): VersionCatalogsExtension = mock {
on { named(name) }.thenReturn(versionCatalog)
}
fun readVersionsFromTomlFile(filePath: String): Map<String, String> {
val toml = Toml().read(File(filePath))
val versionsTable = toml.getTable("versions").toMap()
return versionsTable.mapValues { (_, value) -> value as String }
}
fun readLibrariesFromTomlFile(filePath: String, versions: Map<String, String>): Map<String, MinimalExternalModuleDependency> {
val toml = Toml().read(File(filePath))
val librariesTable = toml.getTable("libraries").toMap()
return librariesTable.map { (libraryName, libraryToml) ->
@Suppress("UNCHECKED_CAST")
val libraryTable = libraryToml as Map<String, Any>
val group = libraryTable["group"] as String
val name = libraryTable["name"] as String
val versionRef = libraryTable["version_ref"] as String
val version = versions[versionRef]
val dependency: MinimalExternalModuleDependency = mock {
on { this.group }.thenReturn(group)
on { this.name }.thenReturn(name)
on { this.version }.thenReturn(version)
}
libraryName to dependency
}.toMap()
}
Thank you very much again for the prompt help and suggestions !