强制Gradle使用较早的依赖版本进行测试

sum*_*tsu 5 java dependency-management gradle build.gradle

有没有一种方法可以构建Gradle构建文件,以强制测试阶段使用比用于编译和打包的依赖关系更早的依赖关系版本?

我正在尝试通过HBaseTestingUtility建立一个HBase迷你集群来测试我的项目。不幸的是,HBaseTestingUtility依赖于旧版本的Guava(似乎可以使用14.0.1),而我项目的其余部分则使用18.0。这是我的构建脚本的摘录,原样(我还使用Unbroken-dome的Gradle测试集插件来创建两组独立的测试):

plugins {
    id 'org.unbroken-dome.test-sets' version '1.2.0'
}

apply plugin: 'java'

ext.verGuava = '18.0'
ext.verGuavaTEST = '14.0.1'

testSets {
    testUnit { dirName = 'test/unit' }
    testIntegration { dirName = 'test/integration' }
}

configurations {
    provided
    provided {
        extendsFrom(compile)
    }
    testIntConf
    testIntConf {
        extendsFrom(provided)
        resolutionStrategy {
            force "com.google.guava:guava:${verGuavaTEST}"
            forcedModules = ["com.google.guava:guava:${verGuavaTEST}"]
        }
    }
}

sourceSets {
    main.compileClasspath += configurations.provided
    testUnit.compileClasspath += configurations.provided
    testUnit.runtimeClasspath += configurations.provided
    testIntegration.compileClasspath += configurations.testIntConf
    testIntegration.runtimeClasspath += configurations.testIntConf
}

dependencies {
    provided "org.apache.hbase:hbase-client:${verHBase}"
    provided "org.apache.hbase:hbase-common:${verHBase}"
    compile "org.testng:testng:${verTestNG}"
    testIntegrationCompile group:'org.apache.hadoop', name:'hadoop-common', version:"${verHadoop}", classifier: 'tests'
    testIntegrationCompile group:'org.apache.hbase', name:'hbase-server', version:"${verHBase}"
    testIntegrationCompile group:'org.apache.hbase', name:'hbase-server', version:"${verHBase}", classifier: 'tests'
    testIntegrationCompile group:'org.apache.hbase', name:'hbase-hadoop-compat', version:"${verHBase}"
    testIntegrationCompile group:'org.apache.hbase', name:'hbase-hadoop-compat', version:"${verHBase}", classifier: 'tests'
    testIntegrationCompile group:'org.apache.hbase', name:'hbase-hadoop2-compat', version:"${verHBase}"
    testIntegrationCompile group:'org.apache.hbase', name:'hbase-hadoop2-compat', version:"${verHBase}", classifier: 'tests'
    testIntegrationCompile group:'org.apache.hadoop', name:'hadoop-hdfs', version:"${verHadoop}"
    testIntegrationCompile group:'org.apache.hadoop', name:'hadoop-hdfs', version:"${verHadoop}", classifier: 'tests'
}

testUnit {
    useTestNG()
    logger.info "@@@ Classpath (UNIT testing): ${classpath.getFiles().collect({it.toString()}).inject('\n') {acc, next -> acc + next + '\n'}}"
    logger.info "@@@ SystemProps (UNIT testing): ${System.getProperties().collect({it.toString()}).inject('\n') {acc, next -> acc + next + '\n'}}"
}

testIntegration {
    useTestNG()
    systemProperty "java.net.preferIPv4Stack", "true"
    logger.info "@@@ Classpath (INTEGRATION testing): ${classpath.getFiles().collect({it.toString()}).inject('\n') {acc, next -> acc + next + '\n'}}"
    logger.info "@@@ SysProps (INTEGRATION testing): ${System.getProperties().collect({it.toString()}).inject('\n') {acc, next -> acc + next + '\n'}}"
}
Run Code Online (Sandbox Code Playgroud)

当我通过此脚本运行构建时,我得到以下输出,该输出似乎表明将Guava 14.0.1 添加到了testIntegration目标的类路径中,而不是替换了 Guava 18.0:

@@@ Classpath (UNIT testing):
/home/user/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/18.0/cce0823396aa693798f8882e64213b1772032b09/guava-18.0.jar
@@@ Classpath (INTEGRATION testing):
/home/user/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/18.0/cce0823396aa693798f8882e64213b1772032b09/guava-18.0.jar
/home/user/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/14.0.1/69e12f4c6aeac392555f1ea86fab82b5e5e31ad4/guava-14.0.1.jar
Run Code Online (Sandbox Code Playgroud)

这种行为可能是意料之中的。API指示将工件添加到要考虑的列表。ResolutionStrategy.force(...)

我该如何指示Gradle从类路径中完全消除testIntegration目标的Guava 18.0?

我尝试更改该sourceSets部分以使用=而不是+ =分配类路径:

sourceSets {
    ...
    testIntegration.compileClasspath = configurations.testIntConf
    testIntegration.runtimeClasspath = configurations.testIntConf
}
Run Code Online (Sandbox Code Playgroud)

就消除Guava 18.0(并保留14.0.1)而言,这具有理想的效果,但是testIntegration由于某种原因,它似乎阻止了Gradle检测源文件的位置,因此testIntegration永远不会编译或执行测试。

我还尝试了一些从继承的配置中清除番石榴的方法,例如:

configurations {
    provided
    provided {
        extendsFrom(compile)
    }
    testIntConf
    testIntConf {
        extendsFrom(provided.copy {
            exclude group:"com.google.guava", module:"guava"
        })
        resolutionStrategy {
            force "com.google.guava:guava:${verGuavaTEST}"
            forcedModules = ["com.google.guava:guava:${verGuavaTEST}"]
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码(以及我尝试过的所有其他变体)确实消除了类路径中Guava工件的重复,但是它们似乎使无效resolutionStrategy,因为即使在中,解析的工件也始终是较新的版本(18.0)testIntegration。

sum*_*tsu 1

虽然可能不是最优雅的解决方案(如果存在的话,我仍然对更干净的解决方案感兴趣),但似乎有效的方法是创建一个完全独立的配置,其中仅包含较新的 Guava 依赖项,然后用于FileCollection#minus减去该配置来自 testIntegration 目标本身的类路径。

首先,创建配置;我在这里称之为它guava,因为它的唯一目的是包含我想从类路径中排除的 Guava 18.0 工件testIntegration:

configurations {
    guava
    ...
}
Run Code Online (Sandbox Code Playgroud)

(配置应包含隔离的 Guava 工件,因此不应包含任何 extendsFrom其他配置。)

其次,将要从类路径中排除的工件添加到新创建的配置的依赖项列表中。在本例中,我知道主要配置将 com.google.guava:guava 解析为版本 18.0,因此我将对该版本的 Guava 的依赖项添加到配置中guava:

dependencies {
    guava group:'com.google.guava', name:'guava', version:"${verGuava}"
    ...
}
Run Code Online (Sandbox Code Playgroud)

第三,在您想要强制使用早期版本的依赖项的 Gradle 目标中调用FileCollection#minuson classpath,以排除较新的依赖项。我testIntegration上面的块被这样改造:

testIntegration {
    classpath = classpath.minus(configurations.guava);
    ...
}
Run Code Online (Sandbox Code Playgroud)

因此,它在 Gradle 构建中的工作原理的快速总结是:

  1. 创建一个extendsFrom主配置(例如compile)的配置,然后使用ResolutionStrategy#force并将早期版本的依赖项添加到该配置的依赖项列表ResolutionStrategy#forcedModules中。
  2. 创建不从任何内容扩展的第二个配置,旨在保存较新版本的依赖项。
  3. 在 中sourceSets,将步骤 1 中创建的配置附加到您希望强制使用早期依赖项版本的目标的compileClasspath和。runtimeClasspath
  4. 在 中dependencies,添加较新版本的工件作为步骤 2 中创建的配置的依赖项。
  5. 由于步骤 1 和 3 中采取的操作,classpath目标块中的 现已包含默认依赖项与早期版本的并集。要删除较新的版本,请设置classpath为classpath.minus(newerDependencyConfiguration),其中newerDependencyConfiguration是在步骤 2 中创建的版本。