Java单元测试实践-22.Gradle资源文件与配置参数动态替换

《JUnit实战 第三版》资源文件使用指南:从环境搭建到深度实践 单元测试是保障软件质量的核心实践,其原理在于通过隔离代码单元并验证其行为是否符合预期,从而在开发早期发现缺陷。JUnit作为Java领域最主流的单元测试框架,其技术价值在于提供了标准化的测试生命周期管理、丰富的断言库以及灵活的扩展机制,能够显著提升代码的可维护性与可靠性。在实际应用场景中,开发者常需将JUnit与Spring Boot、Mockito等框架集成,以应对复杂的依赖管理和模拟测试需求。本文围绕《JUnit实战 第三版》的配套资源文件,详细解析了其作为实战“弹药库”的多模块Maven项目结构,并提 阅读详情

Java单元测试实践-00.目录(9万多字文档+700多测试示例)
https://blog.youkuaiyun.com/a82514921/article/details/107969340

1. Gradle资源文件与配置参数动态替换

1.1. main模块与test模块资源文件

Gradle任务processResources、processTestResources分别用于复制main模块与test模块的资源文件。

1.1.1. 输入输出文件

在processResources、processTestResources任务中,可以通过getInputs()/getOutputs()方法获取输入输出对应的文件信息。

示例如下:

processResources {
    println "inputs: " + getInputs().getFiles().asList()
    println "outputs: " + getOutputs().getFiles().asList()
}

用于复制main模块资源文件的processResources任务打印结果如下所示,可以看到输入文件为main模块resources目录的全部文件(Gradle脚本中未指定排除特定文件),输出目录为“build\resources\main”。

inputs: [E:\UnitTest\src\main\resources\applicationContext.xml, E:\UnitTest\src\main\resources\base.properties, E:\UnitTest\src\main\resources\com\adrninistrator\dao\sqlmap\TestTable2Mapper.xml, E:\UnitTest\src\main\resources\com\adrninistrator\dao\sqlmap\TestTableMapper.xml, E:\UnitTest\src\main\resources\log4j2.xml]

outputs: [E:\UnitTest\build\resources\main]

用于复制test模块资源文件的processTestResources任务打印结果如下所示,可以看到输入文件为main模块resources目录的全部文件(Gradle脚本中未指定排除特定文件),输出目录为“build\resources\test”。

inputs: [E:\UnitTest\src\test\resources\applicationContext.xml, E:\UnitTest\src\test\resources\applicationContext_replace.xml, E:\UnitTest\src\test\resources\base.properties, E:\UnitTest\src\test\resources\base_replace.properties, E:\UnitTest\src\test\resources\entityGen\jpa_modify_type.properties, E:\UnitTest\src\test\resources\entityGenConfig\entityGenConfig.yml, E:\UnitTest\src\test\resources\log4j2.xml, E:\UnitTest\src\test\resources\springhibernate\springhibernate.xml, E:\UnitTest\src\test\resources\sql\create_table.sql, E:\UnitTest\src\test\resources\sql\set_schema.sql, E:\UnitTest\src\test\resources\unit_test_config.groovy]

outputs: [E:\UnitTest\build\resources\test]

以上代码也可添加到compileJava、compileTestJava任务中,用于查看main模块与test模块编译产生的类文件的输出目录,默认情况下分别为“build\classes\java\main”“build\classes\java\test”;若项目使用了Lombok等插件,main模块与test模块类的输出目录还会分别增加“build\generated\sources\annotationProcessor\java\ma
in”“build\generated\sources\annotationProcessor\java\te
st”。

1.1.2. Gradle test任务执行时使用的资源文件

使用Gradle test任务执行测试类时,会优先使用test模块的资源文件,对于main模块与test模块均存在的同名资源文件,会使用test模块中的资源文件;对于仅在main模块中存在的同名资源文件,会使用main模块中的资源文件。

  • 查看GradleWorkerMain进程的classpath系统属性

使用jinfo命令查看GradleWorkerMain进程的java.class.path系统属性,main模块与test模块的相关目录如下所示。

build\classes\java\test
build\resources\test
build\classes\java\main
build\resources\main

可以看到test模块的类目录build\classes\java\test在main模块的类目录build\classes\java\main之前,test模块的资源文件目录build\resources\test在main模块的资源文件目录build\resources\main之前。

  • 在测试类中获取资源文件路径

applicationContext.xml文件在main模块与test模块的resources目录中均存在,在TestConf类中打印classpath中的applicationContext.xml文件路径,可证明使用了test模块中的资源文件,例如/E:/UnitTest/build/resources/test/applicationContext.xml”。

TestTableMapper.xml文件仅在main模块的resources目录中存在,在TestConf类中打印classpath中的TestTableMapper.xml文件路径,证明使用了main模块中的资源文件,例如“/E:/UnitTest/build/resources/main/com/adrninistrator/dao/sqlmap/TestTableMapper.xml”。

1.1.3. test模块资源文件设置

由于Gradle test任务执行时,优先使用test模块中的资源文件,可将main模块中的资源文件拷贝至test模块资源目录中并修改,使Gradle在执行测试类时使用test模块中的资源文件。

  • 使用与main模块不同的配置参数

test模块中的资源文件可以使用与main模块不同的配置参数,例如访问不同的依赖环境、使用不同的日志打印参数等。

  • 提高测试效率

test模块中的资源文件可将线程池大小调小,或将与测试无关的定时任务禁用,减少测试时的性能与时间开销。

1.2. Gradle脚本动态替换配置参数

1.2.1. 适用场景

动态替换配置参数的功能,可在以下场景使用(包括但不限于以下场景)。

  • 生成发布包时修改配置参数

生成不同环境使用的发布包时,可将配置参数替换为对应环境的参数。例如区分生产环境与测试环境。

  • 执行测试时修改配置参数

执行测试时,可将配置参数替换为对应环境的参数。例如在本地开发环境执行测试时,访问MySQL数据库;在CI服务器执行测试时,访问本机的H2数据库。

以下方法支持对main模块与test模块的文件内容进行动态替换。

1.2.2. 在filter中使用ReplaceTokens

  • filter()方法

参考Gradle对于替换文件内容的说明,“Filtering file content (token substitution, templating, etc.)”( https://docs.gradle.org/current/userguide/working_with_files.html#sec:filtering_files )。

使用文件内容过滤器,可以在复制文件时替换文件的内容。

filter()方法有两个变种,它们的行为不同:

一个使用FilterReader并与Ant过滤器(例如ReplaceTokens)一起使用;

另一个使用闭包或Transformer定义源文件每一行的转换。

请注意,以上两个变种都假定源文件是基于文本的。将replaceTokens类与filter()一起使用时,结果是模板引擎用定义的值替换@tokenName@形式的标记(Ant样式的标记)。

以上Gradle文档对于提供的示例如下:

filter(ReplaceTokens, tokens: [copyright: '2009', version: '2.3.1'])
  • ReplaceTokens类

参考ReplaceTokens类的API文档( https://ant.apache.org/manual/api/org/apache/tools/ant/filters/ReplaceTokens.html ),可以看到ReplaceTokens类间接继承自FilterReader类,因此可在filter()方法中使用。

查看Ant的org.apache.tools.ant.filters.ReplaceTokens类源码( https://github.com/apache/ant/blob/master/src/main/org/apache/tools/ant/filters/ReplaceTokens.java )。

其中包含setTokens()方法,参数为Hashtable<String, String> hash,该方法用于设置需要替换的标记Map,参数hash的key对应需要替换的标记,value为需要替换的值,参数hash不能为null。

根据以上内容可知,Gradle脚本filter()方法中的tokens参数用于设置ReplaceTokens类的属性,tokens属性支持Hashtable<String, String>类型。在Gradle脚本的filter()方法中使用ReplaceTokens类时,tokens参数也可以使用字符串形式"tokens",更容易理解。

1.2.3. 使用ConfigSlurper读取Groovy配置文件

上述Gradle文档中提供的替换文档的示例中,需要替换的标记及替换后的值,均声明在Gradle脚本中,也支持从配置文件读取。

  • ConfigSlurper类

参考Groovy的ConfigSlurper类( https://docs.groovy-lang.org/latest/html/documentation/#_configslurper )。

ConfigSlurper是一个工具类,可用于读取Groovy脚本形式的配置文件。Groovy配置文件与Java使用的.properties文件类似,ConfigSlurper允许使用点符号和闭包范围的配置值以及任意对象类型。

ConfigSlurper类的parse()方法可用于返回groovy.util.ConfigObject实例。ConfigObject是特殊的java.util.Map实现,它返回配置的值或新的ConfigObject实例,不可能为null。

Groovy配置文件示例如下,属性environments.development.app.port值为8080。

environments {
    development {
        app.port = 8080
    }

    test {
        app.port = 8082
    }

    production {
        app.port = 80
    }
}

在创建ConfigSlurper对象时,指定所需的第二级元素名称,调用parse()方法可获取到第二级元素下的元素。

例如对应上述Groovy配置文件,使用new ConfigSlurper(‘development’).parse()方法,可以获取到environments.development元素的值,即app.port = 8080。

"environments"是内置的,使用registerConditionalBlock()方法可以注册其他的方法名称,该方法的参数为“String blockName, String blockValue”,应分别指定第一级元素名称,与所需的第二级元素名称。示例如下:

def slurper = new ConfigSlurper()
slurper.registerConditionalBlock('environments', 'development')

参考ConfigSlurper类的API文档( http://docs.groovy-lang.org/latest/html/gapi/groovy/util/ConfigSlurper.html ),可以看到存在参数为“URL scriptLocation”的parse()方法,可指定File对象toURI().toURL()返回的URL对象,用于从指定的文件读取配置信息。

  • ConfigObject类

参考 https://docs.groovy-lang.org/latest/html/documentation/#_configslurper 。

在与Java集成时,可以使用toProperties()方法将ConfigObject转换为java.util.Properties对象。请注意,在将配置值添加到新创建的Properties实例时会将其转换为String实例。

因此ConfigObject对象的toProperties()方法返回值,可在Gradle脚本的filter()方法作为ReplaceTokens类的tokens参数使用,即可以通过Groovy配置文件指定文件内容替换的相关数据。

参考groovy.util.ConfigObject类的API文档( http://docs.groovy-lang.org/latest/html/gapi/groovy/util/ConfigObject.html )。

使用ConfigObject类的toProperties()方法,可以转换为java.util.Properties格式,会预先展平树结构。

假如有以下Groovy配置文件:

a {
    b {
        c1 {
            d1 = 'string'
            d2 = 123
            d3 = 1.23
            d4 = true
        }
        c3 = 1.23
        c4 = true
    }
}

使用以下代码读取上述Groovy配置文件的内容,获取ConfigObject对象后,调用toProperties()方法获取Properties对象。

Properties properties = new ConfigSlurper().parse(file("xxx.groovy").toURI().toURL()).toProperties()
for (Map.Entry entry : properties.entrySet()) {
    println(entry.getKey() + " *" + entry.getValue() + "* " + entry.getValue().getClass().getSimpleName())
}

执行结果如下:

a.b.c3 *[true, 123, string]* String
a.b.c2 *1.23* String
a.b.c1.d4 *true* String
a.b.c1.d3 *1.23* String
a.b.c1.d2 *123* String
a.b.c1.d1 *string* String

可以看到生成的Properties对象的key为Groovy配置文件中的数据的层级关系,每一级元素之间使用“.”分隔,value类型均为String。

对ConfigSlurper对象使用registerConditionalBlock()方法指定需要获取数据的名称,如下所示:

ConfigSlurper configSlurper = new ConfigSlurper()
configSlurper.registerConditionalBlock("a", "b")

执行结果如下:

c1.d4 *true* String
c3 *[true, 123, string]* String
c1.d3 *1.23* String
c2 *1.23* String
c1.d2 *123* String
c1.d1 *string* String

可以看到获取的Properties对象的key为a.b之下层级的元素。

1.2.4. 使用自定义标识符

使用ReplaceTokens类对文件内容进行替换时,默认使用“@”作为替换标记,待替换的原始数据格式为“@tokenName@”。例如待替换内容配置文件中包含“@a.b.c@”,Groovy配置文件中包含参数a.b.c=value,则执行替换操作后,被替换内容的配置文件中,“@tokenName@”会变成“value”。

查看ReplaceTokens类的API文档( https://ant.apache.org/manual/api/org/apache/tools/ant/filters/ReplaceTokens.html ),可以看到包含setBeginToken()、setEndToken()方法,分别用于指定开头及结尾的替换标志。

假如ReplaceTokens类默认的替换标志“@tokenName@”不满足使用要求时,可以通过在Gradle脚本的filter()方法指定ReplaceTokens类的beginToken与endToken属性。如下所示,将替换标志设置为“@@tokenName##”的形式。

filter(org.apache.tools.ant.filters.ReplaceTokens,
        "tokens": configObject.toProperties(),
        "beginToken": "@@",
        "endToken": "##")

1.2.5. 配置文件中文乱码问题

在Gradle脚本的filter()方法使用ReplaceTokens类对文件内容进行替换时,编码为UTF-8的文件中的中文内容在替换后会出现乱码,使Gradle执行时使用UTF-8编码,可解决该问题:

当使用Gradle Wrapper时,在项目中的gradlew.bat/gradlew文件中,在DEFAULT_JVM_OPTS参数增加"-Dfile.encoding=UTF-8";

当不使用Gradle Wrapper时,在Gradle安装目录的bin/gradlew.bat/gradlew文件中,在DEFAULT_JVM_OPTS参数增加"-Dfile.encoding=UTF-8"。

进行以上配置后,在Windows环境执行Gradle命令时,输出的中文会出现乱码(例如编译过程中的提示信息),这是因为Windows批处理窗口的默认编码不是UTF-8。

可在Windows批处理窗口执行“chcp 65001”命令,将编码设置为UTF-8,并将字体修改为True Type字体"Lucida Console",大部分中文不再乱码,但会出现重复。

1.2.6. 示例项目配置文件使用

在示例项目中,根据不同的测试模式(测试验证的内容不相同),需要使用不同的参数配置,在Gradle脚本中根据代表测试模式的参数,对资源文件内容进行替换,如下所示。

  • 根据JVM参数决定使用的参数

在unittest.gradle脚本的ext块中,使用执行Gradle任务时JVM参数中的“testMode”参数,作为当前测试模式,决定当前使用的配置参数。

当未指定该参数时,使用默认测试模式“default_mode”。

在执行Gradle任务时指定“testMode”参数示例如下:

gradlew test -DtestMode=fast
  • Groovy配置文件

在src/test/resources/unit_test_config.groovy配置文件中,配置了不同的测试模式对应的配置参数,第一级元素名称为“test_mode”,第二级元素代表不同的测试模式,如下所示:

test_mode{
    xxx {
        jdbc {
            driver = 数据库驱动名称
            url = 数据库地址
            username = 数据库用户名
            password = 数据库密码
        }
        import_jpa = 引入JPA配置
        test_include = 测试类包含范围(数组)
        test_exclude = 测试类排除范围(数组)
        jacoco_include = 生成代码覆盖率的类包含范围 ( 数组 )
        jacoco_exclude = 生成代码覆盖率的类排除范围 ( 数组 )
    }
}
  • 查看示例支持的testMode参数

unittest.gradle脚本中包含showTestMode任务,用于显示支持的测试模式。

  • 读取Groovy配置文件

在unittest.gradle脚本的ext块中,对Groovy配置文件进行了读取,如下所示:

ext {
    testConfigFilePath = "src/test/resources/unit_test_config.groovy"
    
    configSlurper = new ConfigSlurper()
    configSlurper.registerConditionalBlock("test_mode", testMode)

    configObject = configSlurper.parse(file(testConfigFilePath).toURI().toURL())
}
  • 替换配置文件内容

使用Gradle的test任务执行示例工程单元测试时,需要在使用不同的测试模式时,替换应用程序使用的applicationContext.xml、base.properties配置文件中的参数值。

若以上配置文件中包含替换文件内容时使用的标记,在通过IDE执行单元测试时,替换文件内容的操作不会执行,会导致应用程序无法正确读取配置文件。

因此新增配置文件applicationContext_replace.xml、base_replace.properties,在以上文件中指定替换文件内容时使用的标记,如下所示:

jdbc.driver=@jdbc.driver@
jdbc.url=@jdbc.url@
jdbc.username=@jdbc.username@
jdbc.password=@jdbc.password@

在unittest.gradle脚本的processTestResources任务中,在复制test模块资源文件“applicationContext_replace.xml”与“base_replace.properties”时,在filter()方法中指定通过ReplaceTokens类对文件内容进行替换,使用ext块中获取的configObject变量作为“tokens”参数,如下所示:

processTestResources {
    from(sourceSets.test.resources) {
        include "applicationContext_replace.xml", "base_replace.properties"
        filter(org.apache.tools.ant.filters.ReplaceTokens, "tokens": configObject.toProperties())
    }
}

filter()方法支持在from()方法中使用,或直接在processTestResources等任务中使用。

在from()方法中,也可不使用include指定需要替换内容的文件范围,即对test模块的全部资源文件均尝试进行替换(内容不含标识的文件不会被替换)。

sourceSets.test.resources也可使用sourceSets.test.resources.srcDirs。

  • 覆盖配置文件

在对配置文件applicationContext_replace.xml、base_replace.properties的文件内容进行替换后,需要覆盖应用程序使用的对应配置文件applicationContext.xml、base.properties,在processTestResources任务的doLast Action中,执行替换操作,如下所示:

processTestResources {
    doLast {
        file('build/resources/test/base.properties').renameTo(file('build/resources/test/base.properties.bak'))
        file('build/resources/test/base_replace.properties').renameTo(file('build/resources/test/base.properties'))
        file('build/resources/test/applicationContext.xml').renameTo(file('build/resources/test/applicationContext.xml.bak'))
        file('build/resources/test/applicationContext_replace.xml').renameTo(file('build/resources/test/applicationContext.xml'))
    }
}

1.2.7. 获取Groovy配置文件参数值

使用ConfigSlurper类读取Groovy配置文件中的数据时,获得的ConfigObject对象中包含了原始类型的数据(使用toProperties()方法时参数值类型会变为String)。

假如存在以下Groovy配置文件:

a {
    b {
        c1 {
            d1 = 'string\\1\'2'
            d2 = 123
            d3 = 1.23
            d4 = true
        }
        c2 = 1.23
        c3 = [true, 123, 'string']
    }
}

可通过以下代码进行读取:

ConfigObject configObject = new ConfigSlurper().parse(file("src/test/resources/groovyConfig/test.groovy").toURI().toURL())

ConfigObject configObjectA = configObject.get("a")
println "a: " + configObjectA + " " + configObjectA.getClass().getSimpleName()

ConfigObject configObjectB = configObjectA.get("b")
println "b: " + configObjectB + " " + configObjectB.getClass().getSimpleName()

ConfigObject configObjectC1 = configObjectB.get("c1")
println "c1: " + configObjectC1.getClass().getSimpleName()
for (Map.Entry entry : configObjectC1.entrySet()) {
    println "c1: " + entry.getKey() + " " + entry.getValue() + " " + entry.getValue().getClass().getSimpleName()
}

def c2 = configObjectB.get("c2")
println "c2: " + c2 + " " + c2.getClass().getSimpleName()

List c3 = configObjectB.get("c3")
println "c3: " + c3 + " " + c3.getClass().getSimpleName()
for (Object o : c3) {
    println "c3: " + o + " " + o.getClass().getSimpleName()
}

执行结果如下所示,可以看到从Groovy配置文件读取的数据保持了原有格式,ConfigObject为嵌套格式,参数值支持String、Integer、BigDecimal、Boolean等类型,支持数组格式。

a: [b:[c1:[d1:string\1'2, d2:123, d3:1.23, d4:true], c2:1.23, c3:[true, 123, string]]] ConfigObject
b: [c1:[d1:string\1'2, d2:123, d3:1.23, d4:true], c2:1.23, c3:[true, 123, string]] ConfigObject
c1: ConfigObject
c1: d1 string\1'2 String
c1: d2 123 Integer
c1: d3 1.23 BigDecimal
c1: d4 true Boolean
c2: 1.23 BigDecimal
c3: [true, 123, string] ArrayList
c3: true Boolean
c3: 123 Integer
c3: string String
1.2.7.1. Groovy特殊字符转义

根据以上现象可知,Groovy配置文件中的“\”“’”需要进行转义。

参考 https://docs.groovy-lang.org/latest/html/documentation/#_escaping_special_characters 。

可以使用反斜杠字符“\”对单引号进行转义,以避免终止字符串文字:

'an escaped single quote: \' needs a backslash'

也可以用双反斜杠对转义字符进行转义:

'an escaped escape character: \\ needs a double backslash'

以下为使用反斜杠作为转义字符的特殊字符:

字符说明
\t制表符tabulation
\b退格键backspace
\n换行
\r回车
\f换页
\\反斜杠
\’单引号字符串中的单引号(对于三重单引号和双引号字符串是可选的)
\"双引号字符串中的双引号(三重双引号和单引号字符串是可选的)

1.2.8. 测试范围设置

若执行测试时需要根据参数区分测试范围时,可将测试范围保存在Groovy配置文件中,与Gradle脚本分离。

test任务中的filter()方法中的setIncludePatterns()/setExcludePatterns()方法参数不支持数组或集合形式,因此不使用。可使用setIncludes()/setExcludes()方法指定需要包含/排除的测试范围,与test任务中的include或exclude参数使用方法类似。

在示例项目中通过以上方式设置测试范围。

在Groovy配置文件中unit_test_config.groovy,配置了测试范围参数,如下所示:

test_include = ['**']
test_exclude = ['adrninistrator/test/testdatabase/**', 'adrninistrator/test/testmock/mybatis/**', '**/TestSuite**']

在unittest.gradle脚本的test任务的doFirst Action中,对测试范围进行了设置,如下所示:

def testInclude = configObject.get("test_include")
if (testInclude != null) {
    setIncludes(testInclude)
}

def testExclude = configObject.get("test_exclude")
if (testExclude != null) {
    setExcludes(testExclude)
}

1.3. 根据Gradle执行的任务改变操作

在某些情况下,需要根据Gradle执行的任务改变操作。例如当Gradle执行的任务包含a时,执行操作1;任务不包含a时,执行操作2。

通过以下代码可以获得Gradle当前执行的任务,可参考示例项目unittest.gradle文件中的gradleArgsContains()方法:

boolean gradleArgsContains(String... expectedArgs) {
    StartParameter startParameter = gradle.getStartParameter()

    if (startParameter.isDryRun()) {
        return false
    }

    List<TaskExecutionRequest> taskExecutionRequestList = startParameter.getTaskRequests()
    for (TaskExecutionRequest taskExecutionRequest : taskExecutionRequestList) {
        List<String> args = taskExecutionRequest.getArgs()
        for (String arg : args) {
            for (String expectedArg : expectedArgs) {
                if (arg.equals(expectedArg)) {
                    return true
                }
            }
        }
    }

    return false
}

以上使用的方法可参考 https://docs.gradle.org/current/dsl/org.gradle.api.tasks.GradleBuild.html#org.gradle.api.tasks.GradleBuild:startParameter 、 https://docs.gradle.org/current/javadoc/org/gradle/StartParameter.html#getTaskRequests-- 、 https://docs.gradle.org/current/javadoc/org/gradle/TaskExecutionRequest.html#getArgs-- 。

示例项目在以下任务中根据Gradle执行的任务改变操作,避免多余的操作:

  • processTestResources任务

在processTestResources任务中,使用gradleArgsContains()方法判断当执行的Gradle任务包含test任务时,才执行替换配置文件中的测试参数等操作。

  • jacocoTestReport任务

在jacocoTestReport任务中,使用gradleArgsContains()方法判断当执行的Gradle任务包含当前任务时,才执行设置需要生成覆盖率的代码范围操作。

参考 https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#getName-- ,可以使用getName()方法获取当前任务的名称。

在其他情况下,也可以判断Gradle执行的任务是否包含指定任务,优化Gradle任务的操作。

1.3.1. 解决processResources任务导致找不到资源文件的问题

某些情况下,在生成项目发布使用的jar包或war包时,只需要包含部分资源文件,可能会在processResources任务中通过include指定需要包含的资源文件,如下所示:

processResources {
	include 'com/**'
}

当进行以上配置后,在IDEA中执行单元测试,或正常的Java类时(可能需要清理out目录),以上配置也会生效,导致找不到所需的资源文件。

例如示例项目的main模块包含以下资源文件:

pic

执行MainServer类的main()方法,提示找不到applicationContext.xml文件,查看out/production/resources目录,仅包含以上配置指定的com目录中的资源文件,不包含其他资源文件。

pic

对processResources任务进行修改,判断Gradle执行的任务包含build、jar、war等任务时才执行include操作,如下所示:

processResources {
    if (gradleArgsContains("build", "jar", "war")) {
        include 'com/**'
    }
}

经过以上配置后,可以解决在IDEA执行单元测试,或正常的Java类时,由于processResources任务的配置导致找不到资源文件的问题。同时,生成项目发布使用的jar包或war包时,也能指定需要包含的资源文件。

再次执行示例项目的MainServer类,可以正常执行。执行“gradlew/gradle jar”命令生成jar包,查看其中包含的资源文件只有com目录中的,证明以上配置可以满足不同情况的需求。

Android Gradle插件开发实战:从原理到自动化构建实践 Gradle作为现代Java和Android项目的核心构建工具,其插件机制是实现构建流程自动化的关键技术。通过理解Gradle构建生命周期的初始化、配置和执行三阶段,开发者可以掌握插件如何介入构建过程。自定义Gradle插件能够将重复性任务固化为可复用的工具,显著提升团队工程效率。在Android开发场景中,插件通过与Android Gradle Plugin(AGP)协同工作,实现对资源处理、代码生成等构建环节的定制化扩展。本文聚焦于Android Gradle插件开发,详细拆解了Project、Task 阅读详情

相关推荐

Android开发构建工具链版本对齐:AGP、Gradle、JDK与Android Studio兼容性全解析

在软件工程实践中,构建工具链的版本管理是保障项目稳定性和团队协作效率的基石。其核心原理在于确保构建工具、编译环境与集成开发环境之间的指令集和API接口相互兼容,避免因版本错配导致的构建失败或行为异常。对于Android开发而言,这一技术价值尤为突出,它能显著提升构建速度、确保构建结果的可复现性,并降低团队协作成本。具体到应用场景,无论是新项目初始化、依赖库升级,还是CI/CD流水线的搭建,版本对齐都是首要步骤。本文聚焦于Android Gradle插件(AGP)、Gradle、JDK及Android Stu

ithome 412

gradle processResources 破坏资源文件

在使用 gradle 管理Springboot项目时,遇到下载 resources下的资源文件(word、excel、模板),结果文件下载成功不能正常打开。花了一点时间,找到当项目build 打包时,文件copy是出现问题,打包到jar包下面的文件就是错的。gradle 有一行配置是这样子的, 去掉该配置后打包之后,文件下载就可以打开。的第一个参数的方法是一个模式字符串。我提供'foo/*。html'作为模式。

qq_43556788的博客 1475

RuoYi-App移动端开发实战:从环境搭建到企业级部署

企业级移动应用开发中,前后端分离架构和Hybrid混合开发模式已成为主流技术方案。RuoYi-App基于Spring Boot和Vue.js技术栈,提供了一套完整的移动端开发解决方案,涵盖用户认证、权限管理等核心模块。通过统一的接口规范和模块化设计,开发者可以快速构建高性能移动应用,同时支持原生与H5的灵活切换。该框架特别适合需要快速迭代的中大型项目,其内置的代码生成器和多环境配置能力能显著提升开发效率。在实际部署时,结合Docker容器化和Nginx反向代理可实现高可用架构,满足企业级应用的高并发需求。

weixin_34357436的博客 458

gradle学习(20)-详解java插件

1.使用 在build.gradle中添加以下语句,表示插入java插件 apply plugin:'java' 2.source sets java插件引入了sourceset这个概念,sourceset将编译时和执行时所要用到的source文件组合在一起,其中包含java的源文件和资源文件,有的插件还包括了groovy和Scala资源文件,sourceset

doctorq 1万+

UE4_用户控件_6_通过控件蓝图内部变量来修改进度条的值

发现控件类图类的引用和用户控类引用不兼容,修改变量类型。我们需要添加到视口,同时设置输入模式。创建按钮的点击事件,当点击时调用角色的自定义事件clearwidget。可以了,但是没有退出控件,没有鼠标,所以改进一下。

Growthofnotes的博客 1236

Vue中实现word文档渲染、在线编辑、保存

Vue3对word文档进行渲染、编辑、保存。word转成html,html转成word

h18749849699的博客 1328

gradle编译打包过程 之 ProcessAndroidResources的源码分析

《利器!Android开发工具之谈》专栏系列:https://blog.csdn.net/column/details/16703.html首先,如何查看gradle源码,我们在项目里依赖com.android.tools.build:gradle即可,如下:compile gradleApi() compile 'com.android.tools.build:gradle:2.3.3'sync......

公众号:BennuCTech 4210

手把手拆解固态电池成本:2030年真能比液态电池便宜30%吗?

本文深度解析固态电池成本结构,探讨2030年实现比液态电池便宜30%的可行性路径。通过拆解正极材料、负极材料和固态电解质的成本演变,分析关键技术对降本的影响,并预测产业链协同效应下的成本优化趋势。文章指出,在材料创新、设备国产化和政策支持等多重因素推动下,固态电池有望在2030年实现显著降本目标。

kite3的博客 403

使用gradlew test进行单元测试【Android】

在模块下的build.gradle中进行相关配置

哈哈哈 1736

Ardupilot无人船(车)mavros自主控制

rover 4.4。

超维空间科技的博客 3230

大数据知识面试题-Hadoop(2022版)

序列号 内容 链接 1 大数据知识面试题-通用(2022版) 待续… 2 大数据知识面试题-Hadoop(2022版) 待续… 3 大数据知识面试题-MapReduce和yarn(2022版) 待续… 4 大数据知识面试题-Zookeepr (2022版) 待续… 5 大数据知识面试题-Hive (2022版) 待续… 6 大数据知识面试题-Flume(2022版) 待续… 7 大数据知识面试题-Hbase(2022版) 待续… 8 大数据知识面试题-sqoop(....

拉格朗日的学习笔记 1万+

SpringMVC之gradle ReplaceTokens替换失败解决方法

之前一直在搞gradle,用groovy配置不同环境参数,然而用org.apache.tools.ant.filters.ReplaceTokens替换一直失败的问题, 结果是项目搭建的问题 只要在项目的src/main/java右键build path->configure output folder选择bulid下的classes->main src/main/resou...

weixin_30755709的博客 660

Altium Designer(AD24/25)如何在PCB设计阶段做阻抗匹配

目前阻抗匹配多直接有制版厂进行,PCB设计师在设计阶段可不关注阻抗设计,在加工阶段只需标注需要控制的阻抗线,制板厂即可按照要求对阻抗线进行调整和控制。但是对于设计师来讲掌握阻抗线的设计方法也是非常必要的。本文介绍Altium Designer(AD24/25)如何在PCB设计阶段做阻抗匹配。

fydar的博客 893

Android应用补丁化架构设计与实现:ReVanced Patches技术深度解析

ReVanced Patches是一个高度模块化的Android应用补丁化框架,通过字节码注入技术为YouTube等主流应用提供广告拦截、界面定制、功能增强等核心能力。该项目采用多层级架构设计,结合Gradle构建系统和模块化补丁开发模式,实现了对Android应用的无缝功能扩展和性能优化。 ## 技术架构概述 ReVanced Patches采用分层架构设计,核心由补丁层、扩展层和资源层构成

gitblog_07963的博客 437

SSD |(二)SSD主控

控制器作为一个片上系统,处理来自用户端的命令并负责管理闪存颗粒。

知识库搭建ing 1961

DeerFlow 系列教程 第二十篇 | 前端定制与二次开发指南

支持的语言定义在locale.tsLocale 检测采用三级降级策略Cookie 优先:检查localeCookie浏览器语言默认值en-US.value;if (locale!DeerFlow 使用管理主题切换,在根布局中通过主题相关的 CSS 变量定义在中(约 10KB)。: boolean;// 是否支持深度思考。

weixin_43674738的博客 223

UniApp安卓原生插件开发指南与实战技巧

原生插件开发是跨平台框架中解决性能瓶颈和功能扩展的关键技术。通过原生代码与JavaScript的桥接,开发者可以突破Web容器的限制,直接调用平台特有API。这种技术特别适用于需要高性能计算或硬件交互的场景,如蓝牙通信、传感器数据采集等。UniApp作为基于Vue.js的跨平台解决方案,其原生插件机制支持Module和Component两种模式,分别对应功能扩展和原生UI嵌入。在实际开发中,合理处理线程通信、内存管理以及性能优化是保证插件质量的重点。本指南详细介绍了从环境搭建到插件发布的完整流程,并提供了蓝

weixin_34116110的博客 418

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

gradle 替换项目里用的properties变量名 [gradle访问带点号的key/value]

背景:最近使用Vault来管理用户名和密码时,需要在项目配置文件vault.properties文件里使用占位符来写入变量名,gradle编译期间填入相关环境的roleId & secretId. vault.properties vault.roleId=${vault.roleId} vault.secretId=${vault.secretId} build....

freeRoad 2435

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

gradle编译项目报错Execution failed for task ‘:bootJar‘,‘:mainClass‘,‘:compileJava‘.

idea导入Gradle管理的SpeingBoot多模块项目,依赖下载不下来,执行编译报错。更换另一种申明方式:buildscript。gradle配置参考。

QQ_hoverer的博客 4414

mac Gradle idea中启动单元测试

gradle 单元测试配置

keep_learn的专栏 898

快速解决 org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:compileJava'

开发中遇到的问题 gradle编译出现org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:compileJava' 如果你也遇到了相同的问题,有望在1分钟之内找到问题所在,往下读吧 如图: 错误图 错误图详情 也可以点开详细信息 org.gradle.api.ta...

liangtianmeng的专栏 1万+

【idea实用系列1】 Gradle 构建任务常见命令详解

IDEA Gradle 构建任务常见命令详解

wang2leee的博客 3434

Ant类型之FilterChain

FilterChain是一组FilterReader,用户可以通过继承java.io.FilterReader类来定义自己的FilterReader。通过使用filterreader元素可以轻松的将自定义的FilterReader做为嵌套元素插入到filterchain中。通过FilterChain,ant可以灵活的进行数据的转换。支持FilterChain的task有:concat、copy、loadfile、loadproperties、move。过滤器链是通过嵌套0到多个过滤器形成的。

荣耀之路 1542

gradle学习(8)-gradle的命令行

(以下所有内容都是根据官方文档学习。如果你对gradle有兴趣,希望你能直接学习官方文档,官方文档是最权威的。而且这样才能产生2份有意义的学习资料) 1.执行多个任务 在命令行下,要想执行多个任务,可以在gradle关键字后面跟多个任务名,之间要用空格隔开,执行的顺序按照你的输入的顺序执行。例如我们要执行编译和测试的任务。可以在命令行下输入: D:\GRADLE~2\

doctorq 5245

Gradle配置SpringBoot多环境打包

Gradle Task 实现使用Gradle配置SpringBoot多环境打包,首先要搞清楚Gradle的Task。本文中仅介绍用需要注意的点。具体的可以查看其他博主的文章或者查看官方文档。 首先是第一个问题:task中的代码一定是在task执行时运行的吗? 来看下面两个任务 task t1 { println "t1" } task t2 { println "t2" } 无论我们调用 gradle t1 还是 gradle t2 这两个 println 都会打印。如下: 这是因为其

jaune162的专栏,最新动态请关注:https://jaune162.blog 4910

75Gradle学习笔记

Gradle学习笔记 文章目录1 Gradle简介2 Gradle环境搭建2.1 下载2.2 安装2.3 配置2.3.1 新建变量GRADLE_HOME2.3.2 新建变量GRADLE_USER_HOME2.3.3 添加变量到path2.3.4 修改配置2.3.5 验证安装是否成功2.3.5 Gradle配置本地仓库2.3.6 Gradle配置远程仓库2.3.7 IDEA中配置Gradle2.4 Gradle新建Java工程2.5 Gradle新建Web工程3 groovy基...

tangguanlin2008的博客 1019
上一篇: Java单元测试实践-14.Mock、Spy后Stub Spring成员变量中的方法
下一篇: Java单元测试实践-23.Gradle单元测试日志、报告与JaCoCo代码覆盖率
adrninistrat0r
博客等级 码龄18年 395粉丝 136原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值