이번에는 이전글에 생성하였던 상품을 조회하는 기능을 만들어보자!
먼저 Service Test 파일을 생성해 준다. (경로 src > test > java > com.example.productorderservice > product )
ProductServiceTest.java
package com.example.productorderservice.product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@SpringBootTest
class ProductServiceTest {
@Autowired
private ProductService productService;
@Test
void 상품조회(){
// 상품 등록
productService.addProduct(ProductSteps.상품등록요청_생성());
final long productId = 1L;
// 상품을 조회
final GetProductResponse response = productService.getProduct(productId);
// 상품의 응답을 검증
assertThat(response).isNotNull();
}
}
그리고 상품을 조회할 때 호출하는 ProductService 클래스의 getProduct 메소드를 생성해 준다.
ProductService.java ( getProductResponse 메소드 추가)
package com.example.productorderservice.product;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/products")
class ProductService {
private final ProductPort productPort;
ProductService(final ProductPort productPort) {
this.productPort = productPort;
}
@PostMapping
@Transactional
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();
}
public GetProductResponse getProduct(final long productId) {
final Product product = productPort.getProduct(productId);
return new GetProductResponse(
product.getId(),
product.getName(),
product.getPrice(),
product.getDiscountPolicy());
}
}
위에 ProductService 에서 참조하는 GetProductResponse record를 생성해준다.
(경로 src > main> java > com.example.productorderservice > product )
GetProductResponse .java
package com.example.productorderservice.product;
import org.springframework.util.Assert;
record GetProductResponse(long id, String name, int price, DiscountPolicy discountPolicy) {
GetProductResponse {
Assert.notNull(id, "상품 ID는 필수입니다.");
Assert.hasText(name, "상품명은 필수입니다.");
Assert.notNull(discountPolicy, "할인 정책은 필수입니다.");
}
}
이제 ProductService 에서 호출하는 getProduct에 대하여 Adapter와 Port에 데이터를 처리하기 위한 로직을 작성해 준다.
ProductAdapter.java
package com.example.productorderservice.product;
import org.springframework.stereotype.Component;
@Component
class ProductAdapter implements ProductPort {
private final ProductRepository productRepository;
ProductAdapter(final ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Override
public void save(final Product product) {
productRepository.save(product);
}
@Override
public Product getProduct(long productId) {
return productRepository.findById(productId)
.orElseThrow(() -> new IllegalArgumentException("상품이 존재하지 않습니다."));
}
}
ProductPort.java
package com.example.productorderservice.product;
interface ProductPort {
void save(final Product product);
Product getProduct(long productId);
}
그리고 ProductApiTest.java 안에 private 로 선언되어 있던 상품등록()과 상품등록요청_생성()메소드를 따로 빼내어 별도의 클래스로 만들어 준다. (상품 처리에 대한 로직을 한곳에 모아서 관리하기 용이)
경로(경로 src > test > java > com.example.productorderservice > product)
ProductSteps.java
package com.example.productorderservice.product;
import io.restassured.RestAssured;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import org.springframework.http.MediaType;
public class ProductSteps {
public static ExtractableResponse<Response> 상품등록요청(final AddProductRequest request) {
return RestAssured.given().log().all()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body(request)
.when()
.post("/products")
.then()
.log().all().extract();
}
public static AddProductRequest 상품등록요청_생성() {
final String name = "상품명";
final int price = 1000;
final DiscountPolicy discountPolicy = DiscountPolicy.NONE;
return new AddProductRequest(name, price, discountPolicy);
}
}
그러면 ProductApiTest.java 클래스 안의 내용은 다음과 같이 바뀌게 된다.
ProductApiTest.java
package com.example.productorderservice.product;
import com.example.productorderservice.ApiTest;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
class ProductApiTest extends ApiTest {
@Test
void 상품등록() {
final var request = ProductSteps.상품등록요청_생성();
// API 요청
final var response = ProductSteps.상품등록요청(request);
assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value());
}
}
ProductServiceTest.java 를 실행해보면 다음과 같이 테이블 생성 후 상품정보를 생성한다.
그리고 나서 상품정보를 조회하는 순서의 로그가 보여진다.
다음글에는 사용자가 HTTP GET요청을 보냈을 때 오늘 구현한 response를 응답에 담아 보내주는 것을 구현해보자
Git : https://github.com/ShinHenry/product-order-service.git
Branch : product_search
출처 : 실전! 스프링부트 상품-주문 API 개발로 알아보는 TDD
'Backend > TDD' 카테고리의 다른 글
[TDD] JPA 적용하기 (0) | 2023.02.16 |
---|---|
[TDD] API 테스트로 전환하기 (0) | 2023.02.15 |
[TDD] POJO를 스프링부트 테스트로 전환하기 (0) | 2023.02.15 |
[TDD] POJO 상품 등록 기능 구현하기 (0) | 2023.02.14 |
[TDD] TDD 구현 실습 - 프로젝트 소개 및 생성 (0) | 2023.02.14 |