2~3. POJO를 SpringBoot테스트로 변환하기 & API 테스트 추가하기

이월(0216tw)·2024년 5월 10일
post-thumbnail
  • 이 글은 인프런 이중석님의 👉[스프링부트-실전-상품주문-tdd] 강의를 들으며 작성하였습니다. (소중한 지식 나눔에 감사드립니다👍)

  • 개인 공부 목적이며 상업적 용도가 아님을 밝힙니다.

  • 강의를 들으며 추가로 궁금한 내용 및 오류를 정리한 글 입니다.


섹션1.상품 등록 API 개발

2. 스프링부트 테스트로 전환하기



질문1. 스프링부트로 변경해서 장점은?

기존 POJO 방식으로 했을때는 테스트에서 모든 필요한 클래스를 선언하고 초기화하여야 했다. (즉, 개발자가 주도적으로 필요한 정보를 관리해야했다)

하지만 SpringBoot는 Spring이 어플리케이션 컨택스트를 설정하고 @Service , @Repository , @Component 등이 붙은 클래스를 빈으로 스프링 컨테이너에 등록한다.

그리고 @Autowired 로 Service를 가져오면 이 Service가 의존하는 빈도 자동으로 주입하게 된다 ( 개발자가 아닌 스프링이 제어를 한다)

질문 1 정리)

Spring 의 IoC(제어의역전) 기술을 이용함으로써 개발자가 필요한 기술을
그때그때 주입하는게 아니라 자동으로 주입할 수 있도록 스프링 컨테이너가 관리하므로 개발의 편의성이 증가한다.



3. API 테스트를 추가하기

기존 상품등록 테스트를 실제 사용자가 http요청으로 상품정보를 보낸 것처럼 API를 추가하고 201(created)를 응답하는 과정을 추가한 내용입니다.


질문1. rest-assured란?

API 작동을 테스트할 수 있도록 의존성을 추가했다.
이 의존성은 RESTful 웹 서비스를 테스트하는 데 사용되는 자바용 라이브러리이다.

rest-assured github (rest-assured 깃허브)

근데 내 경우에 4.4.0은 의존성 오류가 발생했기에 5.4.0으로 대체하니
정상 빌드가 되었다.

testImplementation 'io.rest-assured:rest-assured:4.4.0' //오류

testImplementation 'io.rest-assured:rest-assured:5.4.0' //정상빌드

RestAssured를 이용한 코드 예시

 final ExtractableResponse<Response> response = 
 
 RestAssured.given().log().all() 
                .contentType(MediaType.APPLICATION_JSON_VALUE) 
                .port(getPort())  
                .body(request)                   
                .when().post("/products")   
                .then().log().all().extract();    
  • given() : 요청 정보를 작성해달라
    (예: contentType은 Json , log 찍을게 , 데이터는 body에 넣어보낼게)

  • port() : 요청할 포트 지정

  • when() : HTTP method 와 path(URL)을 작성해달라

  • then() : 응답을 어떻게 처리할지 정해달라
    (예 : 응답결과를 로그로 보여주고

  • log().all() : 모든 요청/응답 세부 정보를 로그로 남겨달라

  • extract() : ExtractableResponse 객체로 추출해달라

  • ExtractableResponse ( import io.restassured.response.ExtractableResponse; )
    응답결과를 객체 형태로 추출해준다.

요약을 하자면, JSON형태로 request를 요청값으로 POST방식으로 /products path로 요청할테니 , 그에 대한 응답을 추출가능한응답객체로 담아서 검증하겠다는 의미이다.

실행 결과 (앗, Connection Refused?)

Request method:	POST
Request URI:	http://localhost:9012/products
...
Headers:		Accept=*/*
				Content-Type=application/json
...
Body:
{
    "name": "상품명",
    "price": 1000,
    "discountPolicy": "NONE"
}


java.net.ConnectException: Connection refused: connect

문제를 확인해보니 현재 ProductApiTest.java 에도 @SpringBootTest 어노테이션이 붙어있고, 이 클래스가 상속하고 있는 ApiTest 도 @SpringBootTest가 붙어있어 겹쳐서 발생한 문제인 것 같다.

<ProductApiTest.java 코드> [현재 ApiTest 클래스 상속중]

<ApiTest.java 코드>

실행 결과 222 (404 not found)

Request method:	POST
Request URI:	http://localhost:61548/products
...
Headers:		Accept=*/*
				Content-Type=application/json
Body:
{
    "name": "상품명",
    "price": 1000,
    "discountPolicy": "NONE"
}
...
HTTP/1.1 404 
{
    "error": "Not Found",
    "path": "/products"
}

당연히 /products 에 대한 컨트롤이 없기때문에 정상적인 리턴값이다.

이제 Controller 하나 추가해보자

@RestController
@RequestMapping("/products")

@Component
class ProductService {
    private final ProductPort productPort;
    ProductService(ProductPort productPort) {
        this.productPort = productPort;
    }

    @PostMapping
    public ResponseEntity<Void> addProduct(@RequestBody final AddProductRequest request) {
        final Product product = new Product(request.name(), request.price(), request.discountPolicy());
        productPort.save(product);

        return ResponseEntity.status(HttpStatus.CREATED).build();
    }

기존 ProductService.java에 @RestController를 이용,
/products 에 대한 요청을 받고 리턴하도록 세팅을 했다.

출력결과 333 . (201 반환 완료)

Request method:	POST
Request URI:	http://localhost:61757/products
...
Headers:		Accept=*/*
				Content-Type=application/json
Body:
{
    "name": "상품명",
    "price": 1000,
    "discountPolicy": "NONE"
}
...
HTTP/1.1 201   //정상 응답 완료! 
Content-Length: 0

정상 처리 완료!



질문2 ) 프로젝트 실행 중 오류 발생

혹시 프로젝트를 실행했을 때

Process 'command 'C:\Program Files\Java\jdk-17\bin\java.exe'' finished with non-zero exit value 1

같은 에러가 난다면 file > settings > build,execution,deplayment > gradle > Build를 Gradle 이 아니라 intelliJ 로 설정한다.

다음 이야기

4. JPA를 적용해보기

profile
#SQLD강사 #AI개발 #AI강사 #개발자 개발도 하고 강의도 하지만 고민을 제일 많이 합니다

0개의 댓글