====== Gradle Java Plugin ======
* [[http://www.gradle.org/docs/current/userguide/java_plugin.html|Java Plugin]]
apply plugin: 'java'
* [[gradle:dependencies|Gradle Dependencies]]
===== Java Project 기본 build.gradle =====
* 기본적인 자바 프로젝트 구성을 도와주는 ''build.gradle''로 Eclipse 프로젝트를 가정하고 있다.
apply plugin: 'java'
apply plugin: 'eclipse'
ext {
javaVersion='1.6'
}
buildDir = 'build'
repositories {
mavenCentral()
}
dependencies {
compile '원하는 모듈'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
task initSrc << {
project.sourceSets*.allSource.srcDirTrees.flatten().dir.each { dir ->
dir.mkdirs()
}
}
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
// 소스 인코딩 지정방법 1
[compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8'
// 소스 인코딩 지정밥법 2
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
javadoc {
options.encoding = 'UTF-8'
}
/* Eclipse 관련 설정들 */
tasks.eclipse.dependsOn cleanEclipse
tasks.eclipse.dependsOn initSrc
eclipse {
classpath {
downloadSources = true
defaultOutputDir = file("${buildDir}/classes/main")
}
}
===== 기본 디렉토리 구조 =====
* Maven과 동일하다.
* 소스 : ''src/main/java'', ''src/main/resources''
* 테스트 : ''src/test/java'', ''src/test/resources''
* 빌드 결과 : ''build/*''
* ''buildDir'' 를 프로젝트에 상대적인 경로로 바꿔줄 수 있다. buildDir = 'target'
* ''export GRADLE_OTPS="-Dorg.gradle.project.buildDir=/path/to/custom-build-dir'' System Property 옵션으로 ''buildDir''을 명령행에서 변경할 수 있다. 명령행에 지정하면 안 됐고, 항상 환경변수로 해야만 했다.
===== 기본 태스크 =====
* ''clean'' : build 디렉토리 삭제
* ''compileJava'', ''compileTestJava'' : 소스 컴파일
* ''assemble'' : 컴파일 하고 jar 생성. 웹 프로젝트일 경우에는 war 생성. 단위 테스트 실행 안함.
* ''check'' : 컴파일하고 단위테스트 실행.
* 자세한 것은 [[http://www.gradle.org/docs/current/userguide/java_plugin.html|Java Plugin]]의 Tasks 참조.
===== 기본 의존성 설정 =====
repositories {
mavenCentral()
}
dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
===== Publishing Jar =====
* [[https://docs.gradle.org/current/userguide/maven_plugin.html|Gradle maven plugin]] 참조
// 저장소를 지정하고,
uploadArchives {
repositories {
flatDir {
dirs 'repos'
}
}
}
''gradle uploadArchives'' 명령으로 퍼블리싱한다.
===== Source Sets =====
* Java 플러그인에는 Source Set이라는 개념이 들어가 있으며, 이는 함께 컴파일과 실행되는 소스 파일들의 그룹을 뜻한다.
* 소스 셋에는 자바 소스 파일과 리소스 파일들이 들어간다.
* 다른 플러그인들이 그루비나 스칼라 소스를 추가할 수 있다.
* 소스 셋은 컴파일 클래스패스와 런타임 클래스패스와 관련된다.
* 소스 셋의 목적은 소스들를 논리적 그룹으로 묶고 그 목적을 설명하는 데 있다. 통합 테스트용 소스셋, API 인터페이스 클래스들, 구현체 클래스들 형태로 구분 가능하다.
* 기본 Java Source Set
* main : 실제 작동 소스코드. 컴파일해서 JAR 파일로 들어간다.
* test : 단위 테스트 소스코드. 컴파일해서 JUnit이나 TestNG로 실행한다.
* 아래에서 자세히 다룸.
===== 프로젝트 레이아웃 =====
자바 프로젝트의 기본 프로젝트 레이아웃은 다음과 같다.
* ''src/main/java'' : 실행 자바 소스
* ''src/main/resources'' : 실행 리소스
* ''src/test/java'' : 테스트 자바 소스
* ''src/test/resources'' : 테스트 리소스
* ''src/소스셋/java'' : 특정 소스 셋의 Java 소스
* ''src/소스셋/resources'' : '특정 소스 셋의 리소스
===== 의존성 관리 =====
다음과 같은 의존성 설정(configurations)이 추가되다. 이 의존성 설정은 자바 플러그인의 태스크들에 의해 참조 된다.
^이름 ^부모 ^사용하는 태스크 ^의미 ^
| compile | - | compileJava | 컴파일 시점 의존성 |
| runtime | compile | - | 실행시 의존성 |
| testCompile | compile | compileTestJava | 테스트를 컴파일할 때 필요한 추가적인 의존성 |
| testRuntime | runtime, testCompile | test | 테스트만 실행할 때 필요한 추가적인 의존성 |
| archives | - | uploadArchives | 해당 프로젝트가 생성한 Artifact(jar 등) |
| default | runtime | - | 이 프로젝트에서 기본으로 사용되는 의존성. 이 프로젝트의 실행시에 필요한 Artifact들과 의존성을 포함한다. |
소스 셋을 추가하면 다음과 같은 의존성 설정도 함께 추가된다.
^이름 ^부모 ^사용하는 태스크 ^의미 ^
| 소스셋Compile | - | compile소스셋Java | 특정 소스셋의 컴파일시점 의존성 |
| 소스셋Runtime | 소스셋Compile | - | 특정 소스셋의 실행시 의존성 |
===== 관례 프라퍼티들 =====
Gradle 문서 참조.
[[http://www.gradle.org/docs/current/dsl/org.gradle.api.plugins.JavaPluginConvention.html|JavaPluginConvention]], [[http://www.gradle.org/docs/current/dsl/org.gradle.api.plugins.BasePluginConvention.html|BasePluginConvention]] 참조.
===== Source Set 다루기 =====
* 프로젝트의 소스 셋은 ''sourceSets'' 프라퍼티로 접근할 수 있다. [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/SourceSetContainer.html|SourceSetContainer]] 타입.
println sourceSets.main.output.classesDir
println sourceSets['main'].output.classesDir
sourceSets {
println "Sources Sets " + main.output.classesDir
}
sourceSets {
main {
println "Source Sets main " + output.classesDir
}
}
sourceSets.all {
println "Iterate all Source Sets : " + name
}
* 위에서 사용한 sourceSets 접근 방식 중 하나로 소스셋에 접근하고 변경할 수 있다.
// main java 소스셋의 디렉토리 추가
sourceSets {
main {
java {
srcDir 'src/java'
}
resources {
srcDir 'src/resources'
}
}
}
저기서 main 은 [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/SourceSet.html|SourceSet]]이다.
저기서 'java'와 'resources'는 [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/SourceDirectorySet.html|SourceDirectorySet]]이며 ''srcDir()''메소드는 소스 디렉토리를 **추가**하는 것이다.
==== Source Set 프라퍼티들 ====
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.SourceSet.html|SourceSet]] 참조.
* 기본적인 프라퍼티들
^프라퍼티 이름 ^Type ^ 기본값 ^설명 ^
|name | String(read-only) | not null | 소스셋 이름 |
|output | [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.SourceSetOutput.html|SourceSetOutput]] | not null | 소스셋의 출력 디렉토리. 컴파일된 클래스와 리소스를 저장할 곳 |
|output.classesDir | File | //buildDir///classes///name// | 소스셋의 클래스를 생성할 디렉토리 |
|output.resourcesDir | File | //buildDir///resources///name// | 소스셋의 리소스를 생성할 디렉토리 |
|compileClasspath | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileCollection.html|FileCollection]] | compile//SourceSet// configuration | 소스를 컴파일할 때 사용할 클래스패스 |
|runtimeClasspath | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileCollection.html|FileCollection]] | output + runtime//SourceSet// configuration | 클래스 실행시의 클래스패스 |
|java | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/SourceDirectorySet.html|SourceDirectorySet]] (read-only) | not null | 소스셋의 자바 소스파일들. *.java 파일만 포함호며 그 외 파일은 제외된다. |
|java.srcDirs | Set, Project.files()에서 사용할 수 있는 모든 값 | [//projectDir///src///name///java] | 자바 소스 파일을 포함하고 있는 소스 디렉토리들, ''srcDir()''로 추가가능 |
|resources | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/SourceDirectorySet.html|SourceDirectorySet]] (read-only) | not null | 소스셋의 리소스들. *.java 파일은 제외된다. 플러그인에 때라 제외되는 목록이 추가된다.|
|resources.srcDirs | Set, Project.files()에서 사용할 수 있는 모든 값 | [//projectDir///src///name///resources] | 리소스를 포함하고 있는 소스 디렉토리들 |
|allJava | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/SourceDirectorySet.html|SourceDirectorySet]] (read-only) | java | 모든 *.java 파일들. 플러그인에 따라 더 추가 될 수 있음 |
|allSource | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/SourceDirectorySet.html|SourceDirectorySet]] (read-only) | resources + java | 모든 소스 파일들(리소스 + *.java). 플러그인에 따라 더 추가 될 수 있음. |
==== main/resources에 java 디렉토리를 추가하면서 *.java 파일은 제외하기 ====
때로는 ''src/main/java'' 를 리소스에 포함시켜야 할 경우가 있다. 개발자들이 resources에 리소스를 넣지 않고 java에 넣고서 클래스와 리소스간의 결합성을 쉽게 파악할 수 있고자 할 경우가 있기 때문이다(iBATIS SqlMapper 사용하는 경우 등).
sourceSets {
main {
resources {
srcDir "${project.projectDir}/src/main/java"
// exclude "**/*.java" : 하지 말것. Gradle에서 문제 없지만 Eclipse에서 문제를 일으킴.
}
}
}
==== 새로운 소스셋 만들기 ====
* ''sourceSets { }'' 블럭 안에 정의하면 된다.
* 소스셋이 추가되면 소스셋 이름으로 시작하는 의존성 설정도 추가된다. 이를 사용해 원하는 대로 의존성 설정을 할 수 있다.
sourceSets {
intTest
}
// 의존성 설정
dependencies {
intTestCompile 'junit:junit:4.8.2'
intTestRuntime 'org.ow2.asm.asm-all:4.0'
}
* 소스셋이 추가되면 소스셋 태스크도 추가된다.
* ''gradle intTestClasses'' 형태로 실행.
* compile//소스셋//Java : 소스셋의 소스 컴파일
* process//소스셋//Resources : 소스셋의 리소스를 클래스 디렉토리로 복사한다.
* //소스셋//Classes : compile//소스셋//Java와 process//소스셋//Resources 실행.
==== 소스셋 태스크 예제 ====
* Jar 묶기 태스크
task intTestJar(type: Jar) {
from sourceSets.intTest.output
}
* Javadoc 생성 태스크
task intTestJavadoc(type: Javadoc) {
source sourceSets.intTest.allJava
}
* 단위 테스트 실행 태스크
task intTest(type: Test) {
testClassesDir = sourceSets.intTest.output.classesDir
classpath = sourceSets.intTest.runtimeClasspath
}
* java/test 소스셋 디렉토리 자동 생성
// eclipse 플러그인 실행시 자동으로 기본 Java 디렉토리 구조를 생성하도록 한다.
task baseDirs << {
sourceSets.each { set ->
set.java.srcDirs.each { dir ->
if (!dir.exists()) dir.mkdirs()
}
set.resources.srcDirs.each { dir ->
if (!dir.exists()) dir.mkdirs()
}
}
if (!buildDir.exists()) buildDir.mkdirs()
}
tasks.eclipse.dependsOn baseDirs
===== Javadoc =====
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html|Javadoc]] Task의 인스턴스
* [[http://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#referenceguide|Javadoc Reference Guide]]
* [[springframework:springdoclet|SpringDoclet]] 참조.
* Javadoc의 프라퍼티들
^태스크 프라퍼티 ^타입 ^기본값 ^
|classpath | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileCollection.html|FileCollection]] | sourceSets.main.output + sourceSets.main.compileClasspath |
|source | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileTree.html|FileTree]] | sourceSets.main.allJava]] |
|destincationDir | File | //docsDir///javadoc]]
|title | String | 프로젝트 이름과 버전 |
* [[http://www.gradle.org/docs/current/javadoc/org/gradle/external/javadoc/StandardJavadocDocletOptions.html|StandardJavadocDocletOptions (Gradle API 1.10)]] 참조하여 javadoc 옵션들 지정.
==== Java 9 HTML5 javadoc ====
* [[java:9|Java 9]]부터 HTML5 Javadoc 생성가능함.
* [[http://mrhaki.blogspot.com/2018/11/gradle-goodness-generate-javadoc-in.html|Gradle Goodness: Generate Javadoc In HTML5]]
javadoc {
options.addBooleanOption('html5', true)
}
==== 멀티 모듈의 소스를 합쳐 Javadoc 생성 ====
* [[http://gradle.1045684.n5.nabble.com/aggregating-javadocs-td1433469.html|aggregating javadocs]]
task javadoc(type: Javadoc) {
source subprojects.collect {project ->
project.sourceSets.main.allJava
}
destinationDir = new File(buildDir, 'javadoc')
// Might need a classpath
classpath = files(subprojects.collect {project ->
project.sourceSets.main.compileClasspath})
}
}
===== Delombok =====
* [[java:lombok|Lombok]] 사용시 delombok 후에 javadoc 생성을 해야 올바르게 나온다. [[https://github.com/maydesk/modules/blob/master/master/build.gradle|참조]]
ext {
lombokVersion = '1.12.4'
}
configurations {
lombok
}
dependencies {
lombok "org.projectlombok:lombok:${lombokVersion}"
}
task delombok {
ext.srcJava = 'src/main/java'
ext.srcDelomboked = "${buildDir}/src-delomboked"
inputs.files file(srcJava)
outputs.dir file(srcDelomboked)
doLast {
// 보통은 configurations.runtime만으로 충분하지만, 가끔 provided 등의 사용자정의 configuration이
// 존재 할 경우 classpath에 존재하지 않는 라이브러리라서 경고를 보여줄 수 있으므로
// 모든 configurations의 의존성을 하나로 모아서 classpath로 지정한다.
def allDependencies = configurations.lombok.asFileTree
configurations.all { configuration -> allDependencies = allDependencies + configuration.asFileTree }
// 구버전 Task class : lombok.delombok.ant.DelombokTask
// 최신버전 Task class: lombok.delombok.ant.Tasks$Delombok
ant.taskdef(name: 'delombok', classname: 'lombok.delombok.ant.Tasks$Delombok',
classpath: configurations.lombok.asPath)
ant.delombok(from: srcJava, to: srcDelomboked, verbose: true,
encoding: 'UTF-8', classpath: allDependencies.asPath)
}
}
* delombok 후 javadoc 생성
javadoc {
dependsOn delombok
source = fileTree(dir: delombok.srcDelomboked, includes: ['**/*.java', '**/*.html'])
options.encoding = 'utf-8'
}
// 불필요한 리소스가 복사되는 경우에 대비해 java/html 파일만 include
===== Clean =====
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.Delete.html|Delete]] Task의 인스턴스
* Clean의 프라퍼티
^태스크 프라퍼티 ^타입 ^기본값 ^
|dir | File | //buildDir// |
===== Resources =====
* ProcessResources 태스크([[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.Copy.html|Copy]] 상속)
* ProcessResources 태스크의 프라퍼티들
^태스크 프라퍼티 ^타입 ^기본값 ^
|srcDirs | files()가 받을 수 있는 모든 값 | sourceSet.resources |
|destinationDir | File | sourceSet.output.resourcesDir |
===== CompileJava =====
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html|JavaCompile]] Task 인스턴스
* ''compileTestJava''에도 공통 적용된다.
* 기본적인 옵션들
^태스크 프라퍼티 ^타입 ^기본값 ^
|classpath | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileCollection.html|FileCollection]] | sourceSet.compileClasspath |
|source | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileTree.html|FileTree]] files()가 받을 수 있는 모든 인자]] | sourceSet.java |
|sourceCompatibility | String | Java 소스의 Java 언어 레벨 (..., 1.4,1.5,1.6,1.7 ...) ''project.sourceCompatibilty'' |
|targetCompatibility | Strign | Java 클래스의 Java 언어레벨 ''project.targetCompatibility'' |
|destinationDir | File | sourceSet.output.classesDir |
|options | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/compile/CompileOptions.html|CompileOptions]] | 컴파일 관련 각종 옵션 설정 |
* compile 태스크는 Ant의 javac 태스크를 호출한다. ''options.useAnt=false''로 바꾸면 Ant를 건너뛰고 Gradle 기본 컴파일러로 수행한다.
* 기본적으로 Gradle 프로세스 안에서 컴파일러를 호출하지만 ''options.fork=true''로 설정하면 독립 컴파일러 프로세스가 뜨게 된다. 성능이 떨어질 수 있다.
* ''options''에서 소스 인코딩 등을 지정할 수 있다.
===== 컴파일 옵션들 =====
아래 값들은 Java 플러그인을 적용한 뒤에 설정해야 한다. 그렇지 않으면 Java 플러그인이 값을 초기화 해 버릴 수도 있다. 아래 값들은 ext 블럭으로 만들면 "안" 된다.
* 자바 소스 인코딩 지정
compileJava.options.encoding = 'UTF-8'
// 혹은
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
* 자바 언어 레벨 지정
// project 단위
sourceCompatibility = '1.6'
targetCompatibility = '1.6'
// compileJava 단위
compileJava {
sourceCompatibility = '1.6'
targetCompatibility = '1.6'
}
* 컴파일러 옵션 지정
compileJava.options.compilerArgs = ["-Xlint:unchecked", "-Xlint:deprecation", ...]
// 혹은
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
* define 사용하여 한번에 여러 [[http://gradle.org/docs/current/javadoc/org/gradle/api/tasks/compile/CompileOptions.html|CompileOptions]] ''options'' 지정
compileJava.options.define(
옵션1: 값,
옵션2: 값, ...
)
===== Test =====
* ''test'' 태스크는 [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.testing.Test.html|Test]]의 인스턴스이다.
* 테스트 소스셋에서 모든 단위 테스트를 자동으로 감지하여 실행한다.
* 테스트 수행이 끝나면 보고서를 생성한다.
* JUnit, TestNG를 지원한다.
* [[https://github.com/radarsh/gradle-test-logger-plugin|radarsh/gradle-test-logger-plugin: A Gradle plugin for printing beautiful logs on the console while running tests]]
==== 테스트 실행 ====
* 테스트는 독립 JVM에서 격리상태로 실행된다.
* ''test.debug'' 프라퍼티를 ''true''로 설정하면 디버그모드로 실행되며 5005 포트로 디버깅할 수 있다.
* 병렬 테스트
* 여러 테스트 프로세스를 동시에 실행한 수 있다.
* ''maxParallelForks'' 프라퍼티로 테스트 프로세스 갯수를 설정할 수 있다. 기본값은 1이다.(병렬 테스트 안함)
* 테스트 프로세스는 ''org.gradle.test.worker'' 시스템 프라퍼티를 설정한다.
* ''forkEvery'' 프라퍼티로 몇개의 테스트를 수행한뒤에 프로세스를 재시작 할지 정할 수 있다. 단위 테스트가 JVM Heap을 너무 많이 소모할 경우 이 값을 작게준다. 기본은 재시작 안함.
* [[http://mrhaki.blogspot.kr/2010/11/gradle-goodness-running-tests-in.html|Gradle Goodness: Running Tests in Parallel]]
test {
if (project.hasProperty('maxParallelForks'))
maxParallelForks = project.maxParallelForks as int
if (project.hasProperty('forkEvery'))
forkEvery = project.forkEvery as int
}
* ''ignoreFailures'' 프라퍼티는 테스트 실패시 행위를 정의한다. 기본값은 ''false''이며 테스트가 실패하면 build를 실표로 표시한다. 단, 실패한 테스트에서 즉시 빌드를 멈추지는 않는다. ''true''일 경우 테스트가 실패해도 멈추지 않고 다음으로 넘어가며 빌드를 진행하고 성공으로 표시한다.
* ''failFast'' : ''true''이면 테스트 실패건에서 즉시 빌드를 중단한다. 테스트 실행시 ''--fail-fast'' 옵션을 줘도 된다.
* ''testLogging'' 프라퍼티는 테스트의 로깅 레벨을 설정한다. 기본적으로 모든 실패한 테스트에 대한 요약 메시지를 보여준다. [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.testing.logging.TestLoggingContainer.html|TestLoggingContainer]] 참조.
* 표준 출력/에러를 화면에 표시하려면 ''testLogging.showStandardStreams = true'' 설정 필요.
==== 성공한 Test 강제 실행(force run test) ====
* 성공한 테스트는 다시 테스트를 실행하면 실행이 안된다.
* 이 때, ''cleanTest'' 태스크를 먼저 실행하고 테스트를 하면 된다.
gradlew cleanTest test
==== System Properties ====
* 시스템 프라퍼티는 ''gradle -D프라퍼티이름=값'' 형태로 지정한다.
* ''//taskName//.single=//testNamePattern//'' 형태를 지정하면 ''testNamePattern''에 일치하는 테스트만 실행된다.
* ''//taskName//''은 멀티프로젝트 패스 형태('':sub1:sub2:test'')로 기술하거나 그냥 태스크 이름만 기술해도 된다.
* ''//testNamePattern//''은 ''**/testNamePattern*.class''형태로 기술한다.
* 패턴은 각 서버 프로젝트에 적용된다. 특정 서브 프로젝트에서 패턴에 매칭되는 테스트가 없으면 예외가 발생한다. 이 경우 패턴에 서브프로젝트를 명시할 수 있다.
gradle -Dtest.single=ThisUniquelyNamedTest test
gradle -Dtest.single=a/b/ test
gradle -DintegTest.single=*IntegrationTest integTest
gradle -Dtest.single=:proj1:test:Customer build
gradle -DintegTest.single=c/d/ :proj1:integTest
==== 테스트 감지 ====
* Test 태스크는 컴파일된 클래스를 분석하여 테스트 클래스를 감지한다. 기본적으로 모든 *.class 파일을 분석한다.
* 추상클래스는 실행 안한다.
* 상위 클래스까지 모두 분석한다.
* ''scanForTestClasses''를 ''false''로 하면 자동감지를 수행하지 않는다. 이 경우 명시적으로 포함/제외 시킨 클래스만 실행한다.
* ''scanForTestClasses=false''이면서 포함/제외 클래스를 명시하지 않으면 기본적으로 ''**/*Tests.class''와 ''**/*Test.class''를 실행하고, ''**/Abstract*.class''는 제외한다.
=== JUnit ===
* JUnit 3, 4 테스트 클래스
* TestCase, GroovyTestCase 상속
* ''@RunWith'' 어노테이션 적용
* ''@Test'' 어노테이션을 가진 메소드가 있는 클래스
=== TestNG ===
* ''@Test'' 어노테이션을 가진 메소드가 있는 클래스
==== 테스트의 분리 - 소스셋이 동일하고 이름으로 구분 - 더이상 사용하지 말 것 ====
* ''*Test'' 와 ''*IntegrationTest''를 분리해서 실행하고자 하는 경우가 있을 수 있다.
* [[http://java.dzone.com/articles/gradle-goodness-running-single|Gradle goodness - Running Single Test 참조]]
* ''include''와 ''exclude''를 사용하고 [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.testing.Test.html|Test]]를 상속하는 또 다른 태스크를 만들어 지정한다.
test {
exclude '**/*IntegrationTest.class'
}
task integrationTest(type: Test, dependsOn: testClasses) {
description = 'Integration test'
group = 'verification'
include '**/*IntegrationTest.class'
testReportDir file("${buildDir}/reports/integration-test")
}
tasks.withType(Test) {
// Test 들의 공통 설정
useJUnit()
maxHeapSize '2048m'
jvmArgs '-XX:MaxPermSize=256m'
testLogging {
events 'started', 'passed'
}
}
==== 테스트의 분리 - 소스셋 분리 - 권장 ====
* [[https://www.petrikainulainen.net/programming/gradle/getting-started-with-gradle-integration-testing/|Getting Started With Gradle: Integration Testing]] 참조. 단, Gradle 버전업에 따른 변경 필요.
// 별도 소스셋 구성
sourceSets {
integrationTest {
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/java')
}
groovy { // for Spock
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/groovy')
}
resources.srcDir file('src/integration-test/resources')
}
}
// integrationTest 전용 configuration 지정.
// integrationTest에만 필요한 의존성 지정 가능.
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
// Task 생성
task integrationTest(type: Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs // 구버전에서는 Dir 단수형으로만 지정됨.
classpath = sourceSets.integrationTest.runtimeClasspath
reports { // reports 는 원하는대로만.
junitXml.enabled = true
html.enabled = true
ignoreFailures = true
}
}
// 의존성 지정으로 check 실행시 자동 테스트
check.dependsOn integrationTest
==== TestSets Plugin 사용 ====
* [[https://www.petrikainulainen.net/programming/gradle/getting-started-with-gradle-integration-testing-with-the-testsets-plugin/|Getting Started With Gradle: Integration Testing With the TestSets Plugin]]
* [[https://www.testwithspring.com/lesson/running-integration-tests-with-gradle-spock-edition/|Running Integration Tests With Gradle – Spock Edition]]
* TBD
==== Convention Values ====
^태스크 프라퍼티 ^타입 ^기본값 ^
|testClassesDir | File | sourceSets.test.output.classesDir |
|classpath | [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileCollection.html|FileCollection]] | sourceSets.test.runtimeClasspath |
|testResultsDir | File | testResultsDir |
|testReportDir | File | testReportDir |
|testSrcDirs | List | sourceSets.test.java.srcDirs |
|jvmArgs | List | [], 문자열 배열로 JVM 옵션을 지정한다. |
|maxHeapSize | String | null, '256m' 형태 |
|systemProperty | 키, 값 | 키, 값 쌍을 인자로 테스트 수행 JVM의 시스템 프라퍼티 지정 |
|include | String[] | [], '''**/*IntgrationTest.class', 'org/foo/**', ...'' |
|exclude | String[] | [], '''**/*IntgrationTest.class', 'org/foo/**', ...'' |
==== TestLogging ====
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.testing.logging.TestLoggingContainer.html|TestLoggingContainer]]
test {
testLogging {
// set options for log level LIFECYCLE
events "failed"
exceptionFormat "short"
showStandardStreams true
// set options for log level DEBUG
debug {
events "started", "skipped", "failed"
exceptionFormat "full"
}
}
}
==== test 제외하기 ====
* build 등을 할 때 테스트를 실행하지 않으려면 ''gradle build -x test'' 처럼 ''-x test'' 옵션을 준다.
==== multi project 테스트 결과 모아보기 ====
[[http://www.gradle.org/docs/current/userguide/java_plugin.html#subProjectsTestReport|Creating a unit test report for subprojects]]
subprojects {
apply plugin: 'java'
// Disable the test report for the individual test task
test {
reports.html.enabled = false
}
}
task testReport(type: TestReport) {
destinationDir = file("$buildDir/reports/allTests")
// Include the results from the `test` task in all subprojects
reportOn subprojects*.test
}
==== Test 완료후에 실패건이 있는지 마킹만 하기 ====
* ''ignoreFailures=true''로 두고 테스트를 진행하고 빌드를 완료하지만, 실패건이 있는지 여부를 나중에 알고자 할 때
* [[git:gitlab|Gitlab]] CI 등에서 실패건이 있어도 후속 작업은 진행하기 위해서 만듬.
test {
ignoreFailures = true
afterSuite { TestDescriptor desc, TestResult result ->
if (result.failedTestCount > 0) {
rootProject.buildDir.mkdir()
File testFailed = rootProject.file("${rootProject.buildDir}/testFailedMarker")
if (!testFailed.exists()) {
testFailed.createNewFile()
testFailed.text = "이 파일이 존재하면 테스트중 실패한 건이 존재한다는 의미입니다."
}
logger.warn("testFailedMarker - {}", result.failedTestCount)
}
}
}
* 다른 stage 에서 ''/build/testFailedMarker'' 파일의 존재여부를 체크해서 존재하면 테스트 실패로 간주한다.
if [ -f "build/testFailedMarker" ]; then
echo "file exists"
exit 1
fi
===== Jar =====
* [[http://gradle.org/docs/current/dsl/org.gradle.api.tasks.bundling.Jar.html|jar]] 태스크는 프로젝트의 클래스와 리소스를 포함하는 JAR 파일을 생성한다.
* 파일명 지정
jar {
archiveName = 'nameWhatIWant.jar'
}
==== Manifest ====
* 모든 jar 혹은 war 객체는 [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/java/archives/Manifest.html|Manifest]]''manifest'' 프라퍼티가 있다.
* 압축 파일이 생성될 때 ''MANIFEST.MF'' 파일이 함께 저장된다.
* MANIFEST.MF 커스터마이징
jar {
manifest {
attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
}
}
* 여러 jar에서 manifest 정보 공유
ext.sharedManifest = manifest {
attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
}
task fooJar(type: Jar) {
manifest = project.manifest {
from sharedManifest
}
}
* Manifest를 병합할 수 있다. 병합할 대상은 다른 ''Manifest'' 객체나 파일이 될 수 있다.
task barJar(type: Jar) {
manifest {
attributes key1: 'value1'
from sharedManifest, 'src/config/basemanifest.txt'
from('src/config/javabasemanifest.txt', 'src/config/libbasemanifest.txt') {
eachEntry { details ->
if (details.baseValue != details.mergeValue) {
details.value = baseValue
}
if (details.key == 'foo') {
details.exclude()
}
}
}
}
}
* Manifest는 ''from''절에 기술한 순서에 따라 병합된다.
* 동일 키가 존재할 경우 이후에 병합된 manifest것이 우선한다.
* ''eachEntry'' 를 통해 [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/java/archives/ManifestMergeDetails.html|ManifestMergeDetails]] 객체를 받아서 조정 가능하다.
* 병합은 jar 생성시점 혹은 ''writeTo'' 아니면 ''effectiveManifest''가 호출되는 시점에 늦은 초기화 방식으로 수행된다.
* manifest 기록
jar.manifest.writeTo("$buildDir/mymanifest.mf")
===== JavaExec =====
Java 클래스를 실행할 때 [[gradle:ant|Ant를 사용하는 방법]]과 [[http://gradle.org/docs/current/dsl/org.gradle.api.tasks.JavaExec.html|JavaExec]]를 사용하는 방법이 있다.
''project.javaexec()'' 메소드를 호출해도 된다. ''javaexec'' 메소드는 Closure를 인자로 받는데 거기 들어가는 내용은 ''JavaExec'' 설정과 같다. [[https://discuss.gradle.org/t/how-to-use-in-gradle-javaexec-with-classpath-dependency/7479|How to use in gradle javaexec with classpath dependency]]
[[gradle:application|Gradle Application Plugin]]도 참조한다.
// 외부 의존성 지정 필요시
configurations {
newConfForJavaexec
}
dependencies {
newConfForJavaexec "xxxx:xxx:1.1"
// ...
}
task someTask(type: JavaExec) {
main = 'xxx.yyy.MainClass'
// 외부 의존성의 Java 클래스 실행시
classpath = configurations.newConfForJavaexec
// 외부 jar가 아닌 현재 프로젝트의 Java 클래스 실행시 - 1번 방식
classpath = configurations.runtime
classpath += sourceSets.main.output
// 외부 jar가 아닌 현재 프로젝트의 Java 클래스 실행시 - 2번 방식
classpath = sourceSets.main.runtimeClasspath
args '인자1', '인자2', .....
systemProperty 'simple.message', 'Hello '
jvmArgs '-Xmx512m'
}
* ''classpath''는 [[http://gradle.org/docs/current/javadoc/org/gradle/api/file/FileCollection.html|FileCollection]] 객체이다.
==== System Properties 전달 ====
Gradle을 통해 실행되는 Java Application에 Gradle의 System Properties를그대로 전달하기 - [[http://mrhaki.blogspot.kr/2015/09/gradle-goodness-pass-java-system.html|Gradle Goodness: Pass Java System Properties To Java Tasks]]
// The run task added by the application plugin
// is also of type JavaExec.
tasks.withType(JavaExec) {
// Assign all Java system properties from
// the command line to the JavaExec task.
systemProperties System.properties
}