토리맘의 한글라이즈 프로젝트 logo 토리맘의 한글라이즈 프로젝트

스프링 클라우드 컨트랙트 공식 레퍼런스를 한글로 번역한 문서입니다.

전체 목차는 여기에 있습니다.

목차


5.1. Prerequisites

Spring Cloud Contract Verifier를 WireMock와 함께 사용하려면 Gradle이나 Maven 플러그인을 사용해야 한다.

프로젝트 안에서 Spock을 사용하고 싶다면 spock-core, spock-spring 모듈을 따로따로 추가해야 한다. 자세한 내용은 Spock 문서를 참고해라.


5.2. Add Gradle Plugin with Dependencies

Gradle 플러그인과 의존성을 추가하려면, 다음과 같은 코드를 작성하면 된다:

Plugin DSL GA versions Plugin DSL non GA versions Legacy Plugin Application
// build.gradle
plugins {
  id "groovy"
  // this will work only for GA versions of Spring Cloud Contract
  id "org.springframework.cloud.contract" version "$\{GAVerifierVersion}"
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:$\{GAVerifierVersion}"
	}
}

dependencies {
	testImplementation "org.apache.groovy:groovy-all:$\{groovyVersion}"
	// example with adding Spock core and Spock Spring
	testImplementation "org.spockframework:spock-core:$\{spockVersion}"
	testImplementation "org.spockframework:spock-spring:$\{spockVersion}"
	testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}
// settings.gradle
pluginManagement {
	plugins {
		id "org.springframework.cloud.contract" version "$\{verifierVersion}"
	}
    repositories {
        // to pick from local .m2
        mavenLocal()
        // for snapshots
        maven { url "https://repo.spring.io/snapshot" }
        // for milestones
        maven { url "https://repo.spring.io/milestone" }
        // for GA versions
        gradlePluginPortal()
    }
}

// build.gradle
plugins {
  id "groovy"
  id "org.springframework.cloud.contract"
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:$\{verifierVersion}"
	}
}

dependencies {
	testImplementation "org.apache.groovy:groovy-all:$\{groovyVersion}"
	// example with adding Spock core and Spock Spring
	testImplementation "org.spockframework:spock-core:$\{spockVersion}"
	testImplementation "org.spockframework:spock-spring:$\{spockVersion}"
	testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}
// build.gradle
buildscript {
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath "org.springframework.boot:spring-boot-gradle-plugin:$\{springboot_version}"
		classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$\{verifier_version}"
        // here you can also pass additional dependencies such as Kotlin spec e.g.:
        // classpath "org.springframework.cloud:spring-cloud-contract-spec-kotlin:$\{verifier_version}"
	}
}

apply plugin: 'groovy'
apply plugin: 'org.springframework.cloud.contract'

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:$\{verifier_version}"
	}
}

dependencies {
	testImplementation "org.apache.groovy:groovy-all:$\{groovyVersion}"
	// example with adding Spock core and Spock Spring
	testImplementation "org.spockframework:spock-core:$\{spockVersion}"
	testImplementation "org.spockframework:spock-spring:$\{spockVersion}"
	testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}

5.3. Gradle and Rest Assured 2.0

기본적으로 클래스패스에 Rest Assured 3.x가 추가된다. 이대신 Rest Assured 2.x를 사용하고 싶다면, 아래 보이는 것처럼 추가해줄 수 있다:

buildscript {
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath "org.springframework.boot:spring-boot-gradle-plugin:$\{springboot_version}"
		classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$\{verifier_version}"
	}
}

dependencies {
    // all dependencies
    // you can exclude rest-assured from spring-cloud-contract-verifier
    testCompile "com.jayway.restassured:rest-assured:2.5.0"
    testCompile "com.jayway.restassured:spring-mock-mvc:2.5.0"
}

이렇게 설정해주면 플러그인에서 자동으로 클래스패스에 Rest Assured 2.x가 있는지 확인하고 그에 따라 import 구문을 수정한다.


5.4. Snapshot Versions for Gradle

settings.gradle에 별도 스냅샷 레포지토리를 추가하면, 빌드에 성공할 때마다 스냅샷 버전을 자동으로 업로드할 수 있다.


5.5. Add stubs

Spring Cloud Contract Verifier는 기본적으로 src/contractTest/resources/contracts 디렉토리에서 스텁stub을 찾는다. 플러그인에선 호환성을 고려해 src/test/resources/contracts에서도 명세contract를 찾지만, 이 디렉토리는 Spring Cloud Contract 3.0.0부터 deprecated되었다.

또 하나 주의할 점은, src/contractTest 경로를 사용하려면, 명세contract 테스트 내에서 사용하는 모든 베이스 클래스를 src/contractTest/{language}로 마이그레이션해야 한다. 이때 {language}는 사용하는 언어에 따라 Java 또는 Groovy로 대체하면 된다.

스텁stub 정의가 담겨있는 디렉토리명을 클래스 이름으로 매핑하며, 각 스텁stub 정의는 하나의 테스트로 취급한다. Spring Cloud Contract Verifier는 테스트 클래스 이름으로 사용할 디렉토리가 적어도 한 depth 이상 포함되어 있다고 가정한다. 여러 depth로 중첩된 디렉토리가 존재한다면, 마지막 디렉토리를 제외한 나머지는 패키지 이름으로 사용한다. 예를 들어 아래와 같은 구조를 사용한다면:

src/contractTest/resources/contracts/myservice/shouldCreateUser.groovy
src/contractTest/resources/contracts/myservice/shouldReturnUser.groovy

Spring Cloud Contract Verifier는 위와 같은 구조에선, 아래 두 개의 메소드를 가진 defaultBasePackage.MyService라는 테스트 클래스를 생성한다:


5.6. Running the Plugin

그래들 플러그인은 check 태스크 전에 실행할 Spring Cloud Contract 관련 태스크를 등록해준다. 즉, 따로 뭘 해주지 않아도 알아서 빌드 프로세스에 포함된다. 테스트만 생성하고 싶다면 generateContractTests 태스크를 실행해라.


5.7. Default Setup

디폴트 그래들 플러그인 세팅에서는 빌드 과정에 다음과 같은 Gradle 설정을 추가한다 (슈도 코드):

contracts {
    testFramework ='JUNIT'
    testMode = 'MockMvc'
    generatedTestJavaSourcesDir = project.file("$\{project.buildDir}/generated-test-sources/contractTest/java")
    generatedTestGroovySourcesDir = project.file("$\{project.buildDir}/generated-test-sources/contractTest/groovy")
    generatedTestResourcesDir = project.file("$\{project.buildDir}/generated-test-resources/contracts")
    contractsDslDir = project.file("$\{project.projectDir}/src/contractTest/resources/contracts")
    basePackageForTests = 'org.springframework.cloud.verifier.tests'
    stubsOutputDir = project.file("$\{project.buildDir}/stubs")
    sourceSet = null
}

def verifierStubsJar = tasks.register(type: Jar, name: 'verifierStubsJar', dependsOn: 'generateClientStubs') {
    baseName = project.name
    classifier = contracts.stubsSuffix
    from contractVerifier.stubsOutputDir
}

def copyContracts = tasks.register(type: Copy, name: 'copyContracts') {
    from contracts.contractsDslDir
    into contracts.stubsOutputDir
}

verifierStubsJar.dependsOn copyContracts

5.8. Configuring the Plugin

디폴트 설정을 변경하고 싶다면, 다음과 같이 그래들 설정에 contracts 스니펫을 추가해주면 된다:

contracts {
	testMode = 'MockMvc'
	baseClassForTests = 'org.mycompany.tests'
	generatedTestJavaSourcesDir = project.file('src/generatedContract')
}

원격 소스에서 명세contract를 다운받으려면, 필요에 따라 아래 스니펫을 이용하면 된다:

contracts {
    // If your contracts exist in a JAR archive published to a Maven repository
    contractDependency {
        stringNotation = ''
        // OR
        groupId = ''
        artifactId = ''
        version = ''
        classifier = ''
    }

    // If your contracts exist in a Git SCM repository
    contractRepository {
        repositoryUrl = ''
        // username = ''
        // password = ''
    }

    // controls the nested location to find the contracts in either the JAR or Git SCM source
    contractsPath = ''
}

여기서는 그래들의 Jar 패키징 태스크를 사용하고 있기 때문에, 몇 가지 옵션과 기능을 이용해 verifierStubsJar로 생성하는 것을 더 확장할 수 있다. 그래들에서 직접 제공하는 네이티브 메커니즘을 이용해 기존 태스크를 다음과 같이 커스텀하면 된다:

여기에선 단순 예시로 git.properties 파일을 verifierStubsJar에 추가해본다.

verifierStubsJar {
    from("$\{buildDir}/resources/main/") {
        include("git.properties")
    }
}

추가로, 3.0.0 버전부터 jar 파일을 배포하는 기본 설정이 비활성화되었다는 것도 참고해라. 그렇기 때문에 이제 원하는 이름으로 jar 파일을 생성하고, 평소대로 Gradle 설정 옵션을 통해 배포하면 된다. 즉, jar 파일을 원하는 대로 커스텀해서 빌드하고 jar 레이아웃과 컨텐츠를 완전히 제어해서 배포할 수 있다.


5.9. Configuration Options

명세contract가 포함된 JAR의 위치를 지정하고 싶다면 아래 프로퍼티들을 사용하면 된다:

다음 프로퍼티를 포함하는 contractRepository { … } 클로저도 제공한다

그래들 플러그인이 제공하는 실험적인 기능들도 사용해볼 수 있다:


5.10. Single Base Class for All Tests

MockMvc에서 Spring Cloud Contract Verifier를 사용하는 경우 (디폴트), 모든 자동 생성 인수 테스트acceptance test에서 사용할 베이스 specification을 생성해야 한다. 이 클래스에서 어떤 엔드포인트를 대상으로 테스트를 진행할지를 정의하면 된다. 그 방법은 다음 예제를 참고해라:

abstract class BaseMockMvcSpec extends Specification {

	def setup() {
		RestAssuredMockMvc.standaloneSetup(new PairIdController())
	}

	void isProperCorrelationId(Integer correlationId) {
		assert correlationId == 123456
	}

	void isEmpty(String value) {
		assert value == null
	}

}

Explicit 모드를 사용한다면, 일반적인 통합 테스트에서 흔히 볼 수 있듯이, 베이스 클래스를 통해 테스트하는 애플리케이션 전체를 초기화할 수 있다. JAXRSCLIENT 모드를 사용하는 경우, 베이스 클래스에 protected WebTarget webTarget 필드도 필요하다. 현재 JAX-RS API를 테스트하려면 웹 서버를 시작하는 방법밖에 없다.


5.11. Different Base Classes for Contracts

명세contract마다 베이스 클래스가 다르다면, 테스트를 자동 생성할 때 어떤 클래스를 상속할지 Spring Cloud Contract 플러그인에 지정할 수 있다. 다음과 같은 두 가지 옵션을 제공한다:

5.11.1. By Convention

컨벤션이 뭐냐면, 예를 들어 src/contractTest/resources/contract/foo/bar/baz/에 명세contract를 저장하고 packageWithBaseClasses 프로퍼티 값을 com.example.base로 설정했다면, Spring Cloud Contract Verifier는 com.example.base 패키지 밑에 BarBazBase 클래스가 있다고 가정한다. 즉, 마지막에 두 depth의 디렉토리가 있다면, 이 두 디렉토리 명에 Base를 붙인 이름의 클래스를 찾는다. 이 규칙은 baseClassForTests보다 우선한다.

5.11.2. By Mapping

직접 정규식을 사용해 명세contract 패키지를 베이스 클래스의 풀네임fully qualified name에 매핑할 수 있다. baseClassMappings에는 목록을 지정해야 하는데, contractPackageRegexbaseClassFQN에 매핑하는baseClassMapping 객체를 추가하면 된다.

다음과 같은 디렉토리에 명세contract가 있다고 가정해보자:

매핑에 실패한 경우를 대비해서 폴백으로 baseClassForTests를 제공할 수 있다. (packageWithBaseClasses를 폴백으로 사용해도 좋다.) 이렇게 하면 src/contractTest/resources/contract/com/에 있는 명세contract로 생성한 테스트는 com.example.ComBase를 상속하고, 나머지 테스트는 com.example.FooBase를 상속하게 된다.


5.12. Invoking Generated Tests

프로듀서provider가 명세contract를 준수하고 있는지 확인하려면 다음 명령을 실행해라:

./gradlew contractTest

5.13. Publishing Stubs to Artifact Repository

바이너리 아티팩트 레포지토리에 스텁stub을 보관하는 경우, Gradle의 publishing 섹션에 verifierStubsJar를 포함시켜야 한다. 아래 설정 예시를 참고해라:

apply plugin: 'maven-publish'

publishing {
    publications {
        maven(MavenPublication) {
            // other configuration

            artifact verifierStubsJar
        }
    }
}

3.0.0 버전부터 내부 스텁stub 배포는 deprecated 되었으며, 기본적으로 비활성화돼 있다. 자체 배포 설정에 verifierStubsJar를 추가하는 것을 권장한다.


5.14. Pushing Stubs to SCM

SCM 레포지토리에 명세contract와 스텁stub을 보관하는 경우, 스텁stub을 레포지토리에 푸시하는 과정을 자동화하고 싶을 수 있다. 이땐 아래 명령어를 실행해 pushStubsToScm 태스크를 호출하면 된다:

$ ./gradlew pushStubsToScm

SCM Stub Downloader 사용하기 페이지에서는, contractsProperties 필드를 이용하는 방법 (e.g. contracts { contractsProperties = [foo:"bar"] }), contractsProperties 메소드를 이용하는 방법 (e.g. contracts { contractsProperties([foo:"bar"]) }), 시스템 프로퍼티나 환경 변수를 통해 전달하는 방법 등, 가능한 모든 설정 방법을 확인할 수 있다.


5.15. Spring Cloud Contract Verifier on the Consumer Side

컨슈머consumer 서비스에서도 프로듀서provider와 똑같은 방식으로 Spring Cloud Contract Verifier 플러그인을 구성해야 한다. Stub Runner를 사용하지 않는다면, src/contractTest/resources/contracts에 있는 명세contract를 복사해간 후 아래 명령어를 실행해 WireMock JSON 스텁stub을 생성해야 한다:

./gradlew generateClientStubs

stubsOutputDir 옵션을 설정해야 스텁stub이 생성된다.

스텁stub이 만들어졌다면, 자동 테스트에서 JSON 스텁stub을 사용해 서비스를 컨슘할 수 있다. 다음 예제를 참고해라:

@ContextConfiguration(loader == SpringApplicationContextLoader, classes == Application)
class LoanApplicationServiceSpec extends Specification {

 @ClassRule
 @Shared
 WireMockClassRule wireMockRule == new WireMockClassRule()

 @Autowired
 LoanApplicationService sut

 def 'should successfully apply for loan'() {
   given:
 	LoanApplication application =
			new LoanApplication(client: new Client(clientPesel: '12345678901'), amount: 123.123)
   when:
	LoanApplicationResult loanApplication == sut.loanApplication(application)
   then:
	loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLIED
	loanApplication.rejectionReason == null
 }
}

위 예제에선 LoanApplicationFraudDetection 서비스를 호출한다. 이 요청은 Spring Cloud Contract Verifier가 생성한 스텁stub으로 구성된 WireMock 서버가 처리한다.


Next :
6. Maven Project
Maven으로 컨슈머-프로듀서 간 컨트랙트 테스트 자동화하기

전체 목차는 여기에 있습니다.

<< >>

TOP