Programing/Framework

spring-boot 신규 프로젝트 BY spring initializr

나모찾기 2021. 4. 28. 15:46

회사에서 신규 프로젝트를 진행하게 되었다.

새 술은 새부대에 부으라는 말 처럼 spring initializr를 이용해서 새 프로젝트를 만들었다.

 

전에 N사에 면접을 봤을 때 어떤 조직장이 코틀린을 써봤냐고 물어본 적이 있었다.
당시 코틀린을 실제 업무 프로젝트에서의 경험이 없어서 사실대로 이야기 했는데 개인적으로 아쉬움이 남았다.

그래서 이번에는 신규 프로젝트

니 Java 대신 Kotlin 으로 해보았으면 바람을 반영했다.

 

그런데 Language를 Kotlin으로 선택을 하고 Gradle Project로 생성을 했더니 build.gradle이 아닌 build.gradle.kts 파일이 생성되었다.

build.gradle.kts

넌 뭐냐 build.gradle.kts

검색을 해보니 이 형태가 Kotlin DSL 이라는 것을 찾을 수 있었다. Gradle에서 전통적으로 Groovy DSL 을 사용했는데 새로운 대체 문법을 제공하는 형태라고 한다.

 

Spock Framework

어제 삽질의 주요 원인이 Spring Boot + Kotlin + Kotlin DSL in Gradle 에 Spock Framework 를 붙이는 것이었다.

의존성에 아래 문장을 추가했다.

dependencies {
	// 
	testImplementation("org.spockframework:spock-core:1.3-groovy-2.5")

그런데 build.gradle.kts 파일이 빨간색 불이 들어오고 빌드가 안되는 문제가 있었다.

다른 블로그를 보니 testImplementation 대신 compile을 사용하고 있었다.

compile은 implementation로 바뀐 것을 알고 있었지만 시험삼아 바꾸어보니 삭선이 그어졌다.

그래서 implementation 로 교체를 했다.

 

기본 테스트 코드는 Spring Boot 프로젝트가 만드는 Application에 대한 테스트를 하기로 했다.

기본적으로 생성되는 테스트 코드는 JUnit 5의 것이다. (확장자가 kt 이긴 하지만)

import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
class ReviewApiApplicationTests {

	@Test
	fun contextLoads() {
	}

}

유사하게 만들면 아래와 같다.

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.ApplicationContext
import org.springframework.test.context.ActiveProfiles
import spock.lang.Specification

@ActiveProfiles("test")
@SpringBootTest
class TestApplicationSpec extends Specification {

    @Autowired
    private ApplicationContext applicationContext

    def "contextLoads"() {
        expect:
        applicationContext != null
    }
}

일단 빌드는 되는데 Spock 테스트 코드가 돌아가지 않는다.

메시지를 보니 테스트가 없다고 나온다??

Execution failed for task ':test'.
> No tests found for given includes: [com.tistory.test.TestApplicationKtIntegrationSpec](filter.includeTestsMatching)

filter.includefilter.includeTestsMatching 에 테스트에 대한 매칭 조건이 있는 것 같은데 이름이 IntegrationSpec이라 매칭이 안되는 걸까???

아래 스크린 샷을 보면 어제 얼마나 삽질을 했는지 한눈에 볼 수 있다.

평소에 브라우저 탭을 최소한으로 유지하는데 검색에 검색에 검색으로 탭이 30개가 넘는다.

 

하다가 너무 늦어서 포기하고 집에서 고민해보니 현재 만드는 것이 MVP로 속도가 관건이었다.

이런 프로젝트 세팅에 시간을 보내기가 아까워서 결단을 했다.

이미 익숙한 Java와 Gradle DSL로 돌아가자....

 

그래서 오늘 다시 spring initializr를 이용해서 새 프로젝트를 만들었다.

그리고 Java 기반에 Spock 을 추가했다.

그런데 어제의 filter.includeTestsMatching 메시지는 없고 테스트 실패가 된다.

 

다만 좀 이상한점은 @SpringBootTest 의 경우 스프링 컨텍스트가 올라오는 로그가 보여야 하는데 그런 것 없이 달랑 assert만 수행된 것이다.

 

혹시나 싶어서 org.spockframework:spock-spring 를 추가해봤다.

이 로고가 보고 싶었던 것이다.

그랬더니 잘 뜬다. 이런 삽...질..

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    runtimeOnly("com.h2database:h2")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.spockframework:spock-core:1.3-groovy-2.5")
    testImplementation("org.spockframework:spock-spring:1.3-groovy-2.5")
}

 

 

혹시나 싶어서 다시 Kt 프로젝트로 Git 을 돌려서 해보니 잘 된다.

원인을 알고 나니 허탈해진다.

 

그런데 어제 filter.includeTestsMatching 가 왜 떴었는지는 아직도 모르겠다.

어제 의심했던 것은 JUnitPlatform이 JUnit5라서 아직 Unit4기반으로 돌아가는 Spock 1.x 버전에서 문제가 있었는줄 알았다.

그래서 useJnit() 도 했는데 그것이 아니었다.

tasks.withType<Test> {
    //useJUnit()
    useJUnitPlatform()
}

filter.includeTestsMatching 문제와 해결책

위에서 언급했던 filter.includeTestsMatching 의 문제에 대해 LuRan 님의 블로그에서 원인을 알게 되었다.

론부터 이야기하면 useJUnitPlatform 이 되어 있다고 해도 JUnit4 에 대한 의존성라이브러가 명시되지 않으면 JUnit4에 대한 Test Suite가 포함되지 않아서 테스트가 되지 않는 문제였다.

 

일부 인프런 등의 임시성 방법은 개발환경에서 테스트를 돌릴 때는 문제가 없지만, 나중에 CI/DI에서 gradle 명령으로 돌리면 테스트가 빠져서 커버리지에 구멍이 생기게 되는 문제가 있다.

 

Spock 1.3 까지는 JUnit 4에 의존하고, Spock 2.x 부터는 JUnit 5를 사용하게 된다.
아직 정식 버전 대신 마일스톤 버전인 2.0-M5가 현재 최신 버전이다. 이 버전을 쓰게 되면 JUnit4 를 추가하지 않아도 가능하다.

선택 옵션들

Spock 1.3

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-mustache")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.junit.vintage:junit-vintage-engine") // 이것이 필요하다.
    testImplementation("org.spockframework:spock-core")
    testImplementation("org.spockframework:spock-spring:1.3-groovy-2.5")
}

 

Spock 2.x Milestone version

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-mustache")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.codehaus.groovy:groovy:3.0.8")  // 이것이 없으면 compileTestGroovy 이 실패한다.
    testImplementation("org.spockframework:spock-core:2.0-M5-groovy-3.0")
    testImplementation("org.spockframework:spock-spring:2.0-M5-groovy-3.0")
}

참고로 2.0-M5 은 groovy-2.5 와  groovy-3.0 둘 중에 선택이 가능하다.

 

Groovy 2.5를 사용하는 경우에 아래와 같은 경고가 떠서 뭔가 아쉬운 느낌이 든다.

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/Users/namo/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.14/f0a005fb21e7bd9b7ebf04cd2ecda0fc8f3be59d/groovy-2.5.14.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

IntelliJ IDEA 문제

IntelliJ IDEA 2020.3 에서는  testImplementation 으로 Spock 의존성이 되어있으면 Spock library not found in the module 라는 메시지가 나타난다.

만약 Fix 를 누르면 testImplementation 와 별개로 implementation을 새로 추가해버린다.

어제 봤던 레퍼런스들

  1. Gradle Kotlin DSL을 무작정 적용해보자! - 개발새발 블로그 :: https://seansation-blog.tistory.com/7
  2. java - Gradle이 추가 소스 세트에서 테스트를 찾을 수 없음 :: https://www.python2.net/questions-498737.htm
  3. kotlin-dsl-samples - GitHub :: https://github.com/gradle/kotlin-dsl-samples/issues
  4. Issues with integration into Gradle/Kotlin project - sonarsource :: https://community.sonarsource.com/t/issues-with-integration-into-gradle-kotlin-project/8922
  5. Muselee 4: Gradle Kotlin DSL :: https://blog.stylingandroid.com/muselee-4-gradle-kotlin-dsl/
  6. [Spock] Spock Framework 이용하기 - 개론편 - https://sabarada.tistory.com/107
  7. The missing migration guide to the Gradle Kotlin DSL - https://github.com/jnizet/gradle-kotlin-dsl-migration-guide
  8. [spring-boot] Junit5 적용기 (ihoneymon) - GitHub :: https://github.com/ihoneymon/spring-boot2-junit5-spock
  9. Gradle Kotlin DSL Primer (gradle) :: https://docs.gradle.org/current/userguide/kotlin_dsl.html
  10. Exclude a transitive dependency with Gradle’s Kotlin DSL (Niel de Wet) :: https://nieldw.medium.com/exclude-a-transitive-dependency-with-gradles-kotlin-dsl-82fb41da67f
  11. Spock Framework - GitHub :: https://github.com/spockframework/spock
  12. 테스트 도중 에러 발생 (inflearn) :: https://www.inflearn.com/questions/15495
  13. Apache Groovy (MVN repository) :: https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-all
  14. Spock Framework Reference Documentation :: https://spockframework.org/spock/docs/1.3/all_in_one.html#_1_3_2019_03_05
  15. testing-kotlin-with-spock (alien11689) - GitHub :: https://github.com/alien11689/testing-kotlin-with-spock
  16. Testing Kotlin With Spock (Part 1): Object (DZone) :: https://dzone.com/articles/testing-kotlin-with-spock-part-1-object
  17. Comparing Testing Library for Kotlin (남경호: Veluexer's Blog):: https://veluxer62.github.io/explanation/comparing-testing-library-for-kotlin/
  18. Kotlin, Spring, & Spock — Lottery! :: https://medium.com/software-tidbits/kotlin-spring-spock-lottery-bac684d5979f
  19. Spring Boot와 Kotlin으로 웹 애플리케이션 구축하기 :: https://infoscis.github.io/2018/08/30/spring-boot-kotlin/
  20. 'Gradle Kotlin DSL' 이야기 (김지헌 - 우아한 형제들) :: https://woowabros.github.io/tools/2019/04/30/gradle-kotlin-dsl.html
  21. spock-example/build.gradle - GitHub :: https://github.com/spockframework/spock-example/blob/master/build.gradle
  22. Integration tests with Gradle Kotlin DSL (stackoverflow) :: https://stackoverflow.com/questions/52904603/integration-tests-with-gradle-kotlin-dsl
  23. Intellij Test 실패 발생시 No tests found for given includes (아라한사) :: https://adunhansa.tistory.com/236
  24. Intellij does not recognize an extra Kotlin SourceSet from gradle configuration (youtrack) :: https://youtrack.jetbrains.com/issue/KT-30807
  25. Spock Framework Core Module » 1.3-groovy-2.5 (MVN) :: https://mvnrepository.com/artifact/org.spockframework/spock-core/1.3-groovy-2.5
  26. [Spock] Spock Framework 이용하기 - 개론편 (차곡차곡 사바라다) :: https://sabarada.tistory.com/107
  27. 주력 개발언어 늘리기: 코틀린(Kotlin) - GitHub :: https://github.com/jiheon0301/migrate-spock-to-spek
  28. .gitignore :: https://www.toptal.com/developers/gitignore
  29. kotlin/.gitignore - GitHub :: https://github.com/JetBrains/kotlin/blob/master/.gitignore
  30. kotlin DSL gradle - QueryDSL 설정 (chartnomy) :: https://velog.io/@gosgjung/kotlin-DSL-gradle-QueryDSL-%EC%84%A4%EC%A0%95
  31. Spring Boot 2.2.4 Gradle 프로젝트 설정 (준드래곤) :: https://jundragon.tistory.com/6
  32. Spring WebFlux (1) (토리맘의 한글라이즈 프로젝트) :: https://godekdls.github.io/Reactive%20Spring/springwebflux/
  33. Spring Rest Docs 적용 :: https://woowabros.github.io/experience/2018/12/28/spring-rest-docs.html
  34. snippetsDir and the kotlin dsl #922 - GitHub :: https://github.com/spring-io/initializr/issues/922