Spring & Spring Boot/Spring Boot

[Spring Boot] Cloud Native Java - Test(3) 스프링 클라우드 컨트랙트

Serina_Heo 2022. 9. 29. 10:56

스프링 클라우드 컨트랙트로 만든 스텁은 컨슈머 주도 계약 테스트에서 사용되는 서비스의 동작을 흉내낸다. 이런 동작은 사용자에게 공개된 REST API 메소드의 동작에 대해 서비스 프로듀서가 설정한 예상이므로 예상 동작이라고 부른다. 각 스텁의 정의 파일은 Controller 클래스 내의 하나의 메소드에 해당한다. UserController 에 하나의 메서드만 있었으므로 스텁 정의 파일도 하나만 작성한다.  스텁 정의 파일 이름은 shouldReturnUser.groovy 다. 스프링 클라우드 컨트랙트로 만든 스텁 정의 파일은 스프링 클라우드 컨트랙트 그루비 언어로 작성된다. 기본적으로 스텁 정의 파일은 테스트 소스 루트의 resources 디렉토리에 저장한다. 

userService 클래스는 사용자 인증 정보 상세 내용을 조회한다.

package demo.user;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.streotype.Service;

import java.security.Principle;
import java.util.Optional;

@Service
public class UserService {
    
    private final UserRepository userRepository;
    
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository. = userRepository;
    }
    
    public User getUserByPrincipal(Principal principal) {
        return Optional.ofNullable(principal)
            .map(p -> userRepository.findUserByUsername(p.getName()))
                .orElse(null);
    }
}

REST 컨트롤러는 정의하는 UserContoller 클래스

package demo.user;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;
import java.util.Optional;

@RestController
@RequestMapping("/uaa/v1")
public class UserController {
    
    private UserService userService;
    private AuthService authService;
    
    @Autowired
    public UserController(UserService userService, AuthService authService) {
        this.userService = userService;
        this.authService = authService;
    }
    
    @RequestMapping(path = "/me")
    public ResponseEntity<User> me(Principal principal) throws Exception {
        return Optional.ofNullable(
            authService.getAuthenticatedUser(principal))
            // principal 이 null 이 아니면 UserService 에서 사용자를 찾는다.
            .map(p -> ResponseEntity.ok()
                    .body(userService.getUserByPrincipal(p)))
                // Principal 이 null 이면 HTTP UNAUTHORIZED 에러를 ResponseEntity 에 담아서 반환한다
                .orElse(new ResponseEntity<User>(HttpStatus.UNAUTHORIZED));
    }
}

스프링 클라우드 컨트랙트로 만든 스텁 정의 파일은 스프링 클라우드 컨트랙트 그루비 언어로 작성된다. 기본적으로 스텁 정의 파일은 테스트 소스 루트의 resources 디렉토리에 저장한다. 

package contracts

org.springframework.cloud.contract.spec.Contract.make {
    request {
        method 'GET'
        url '/uaa/v1/me'
        headers {
            header('Content-Type': consumer(regex('application/*json*')))
        }
    }
    response {
        status 200
        body([
                username    : value(producer(regex('[A-Za-z0-9]+'))),
                firstName   : value(producer(regex('[A-Za-z]+'))),
                lastName    : value(producer(regex('[A-Za-z]+'))),

//@formatter:off
                email       : value(producer(
                        regex('[A-Za-z0-9]+\\@[A-Za-z0-9]+\\.[A-Za-z]+'))),
//@formatter:on
                createdAt   : value(producer(regex('[0-9]+'))),
                lastModified: value(producer(regex('[0-9]+'))),
                id          : value(producer(regex('[0-9]+')))
        ])
        headers {
            header('Content-Type': value(
                    producer('application/json;charset=UTF-8'),
                    consumer('application/json;charset=UTF-8'))
            )
        }
    }
}

[Reference] Cloud Native Java - Josh Long, Kenny Bastani, 책만