< Dev-Kidult />

`Spring cloud contract` 를 배워보자 본문

개발/Back-end

`Spring cloud contract` 를 배워보자

개른이 2020. 8. 14. 17:13

예제(코드)는 깃허브에 있습니다.

 

1. Spring cloud contract 이전은 어떻게 테스트를 했을까

  • 모든 마이크로 서비스를 올리고 엔드 투 엔드 테스트를 한다.
  • 다른 마이크로 서비스 목(mock)을 만들어서 유닛 및 통합 테스트를 한다.

2. 모든 마이크로 서비스를 올리고 엔드 투 엔드 테스트를 했을 때 

 

  장점:

  • 서비스를 시뮬레이터 할 수 있음.
  • 서비스 간 실제 통신을 테스트.

  단점:

  • 테스트를 하기 위해 모든 마이크로 혹은 연관된 서비스들을 배치해야 함.
  • 테스트를 하는 동안 다른 마이크로 서비스는 잠겨 있음. (그동안 아무도 테스트를 하지 못함)
  • 테스트를 하기위한 시간이 오래 걸림
  • 피드백 과정이 오래 걸림
  • 디버깅을 하기가 매우 어려움

3. 다른 마이크로 서비스 목(mock)을 만들어서 유닛 및 통합 테스트를 했을 때

 

  장점:

  • 매우 빠른 피드백
  • 인프라 요구사항이 없음

단점:

  • 목을 만드는 과정이 불안정함
  • 목이 불안정함으로써 테스트는 통과했지만 실제 프로덕트 환경에서는 작동하지 않을 수 있음

 

 

앞에 언급한 단점들을 보완하기 위해 spring cloud contract가 생성되었다고 공식문서는 말하고 있습니다. 

 

다른 서비스에 api를 제공해주는 producer들은 스텁을 생성하게 되는데 그 과정에서 테스트되어 만들어졌기 때문에 목과는 달리 불안정함이 없음을 알 수 있습니다.

또한 해당 api를 소비하는 consumer들은 소비할 api의 서비스를 구동하지 않아도 해당 스텁을 통해 테스트를 할 수 있기 때문에 빠르며, 정확하고 가볍게 테스트할 수 있습니다.

 

4. contract란

 

As consumers of services, we need to define what exactly we want to achieve. We need to formulate our expectations. That is why we write contracts. In other words, a contract is an agreement on how the API or message communication should look.

공식 문서에 따르면 위와 같이 말하고 있습니다.

 

contract는 groovy yaml(yml) kotlin으로 쓸 수 있으며

/foo라고 요청을 하면 bar라고 응답하는 api 있다고 가정해보겠습니다.

Contract.make {
    description "/foo 라고 콜하면 bar를 줄꺼야"
    request {
        method GET()
        url("/foo")
    }
    response {
        body("bar")
        status 200
    }
}

 

grooby는 위와 같이 작성할 수 있으며, 나머지 yaml 및 kotlin은 공식 문서공식 샘플 깃허브를 가면 볼 수 있습니다.

 

5. 예제

모든 예제는 spring kotlin gradle 기반입니다.


Producer

 

먼저 생산자쪽에서는 이와 같이 dependencie 및 plugin을 추가해 줍니다.

buildscript {
    ...
    val verifierVersion: String by extra
    dependencies {
        classpath("org.springframework.cloud:spring-cloud-contract-spec-kotlin:$verifierVersion")
    }
}

plugins {
    ...
    id("org.springframework.cloud.contract") version "2.2.4.RELEASE"
    id("maven-publish")
}

dependencies {
    ...
    testImplementation("org.springframework.cloud:spring-cloud-starter-contract-verifier")
    testImplementation("org.springframework.cloud:spring-cloud-contract-spec-kotlin")
}

contracts {
    baseClassForTests.set("yh.study.producer.ProducerBase") // 1
}

 

1. contracts 플러그인에는 많은 옵션이 있지만 여기서는 contract의 테스트 기본이 되는 베이스 클래스를 하나 만들고 contract 작성 기반 테스트 코드들이 자동으로 만들어지는 걸 선택했습니다.

 

그리고 서비스하고 있는 API들을 위 contract처럼 하나하나 만들어주고 빌드할 경우 contract기반으로 자동으로 테스트 코드들이 생성됩니다.

 

그리고 consumer에게 제공하기 위해서 maven-pulish 플러그인을 쓰게 되는데 이 예제에서는 로컬 메이븐에 저장 및 이용을 하게 할 겁니다.

 

프로젝트 루트에 가셔서 해당 명령어를 치게 되면 로컬 메이븐 레파지토리에 스텁 파일이 생성됩니다.

./gradlew publishToMavenLocal

 


 

Consumer

 

소비자 쪽은 다음과 같은 dependencie 및 plugin을 추가해 줍니다.

dependencies {
    ...
    testImplementation("org.springframework.cloud:spring-cloud-starter-contract-stub-runner")
    testImplementation("org.springframework.cloud:spring-cloud-contract-wiremock")
}

 

테스트를 위한 stub-runner와 contract-wiremock을 추가해 줍니다.

그리고 Producer에서 만든 스텁을 로컬메이븐에서 다음과 같이 가져와줍니다.

 

그리고 테스트클래스 위에 어노테이션을 아래와 같이 채워주시면 됩니다.

 

...
@AutoConfigureStubRunner(
    stubsMode = StubRunnerProperties.StubsMode.LOCAL,
    ids = ["{project.group}:{project.artifact}:+:stubs:{api.port}"] // 1
)
...

 

1.

  • {project.group} 에는 해당 프로젝트 그룹명을
  • {project.artifact} 에는 해당 프로젝트 아티팩트를
  • {api.port} 에는 해당 API 포트 넘버를 

각각 프로젝트 설정에 맞게 내용을 채워 주시면 됩니다.

 

그리고 스텁의 API를 호출하는 컨트롤러 혹은 서비스 테스트를 만들어주면 완성입니다.

반응형
Comments