새로운 상품이 등록되면 가장 최근 상품번호 + 1 한값을 ProductNumber로 가지는 데이터를 생성한다.
@DisplayName("가장 마지막 저장한 상품의 상품번호를 읽어온다.")
@Test
void findLatestProductNumber() {
// given
String targetProductNumber = "003";
Product product1 = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000);
Product product2 = createProduct("002", HANDMADE, HOLD, "카페라떼", 4500);
Product product3 = createProduct(targetProductNumber, HANDMADE, STOP_SELLING, "팥빙수", 7000);
productRepository.saveAll(List.of(product1, product2, product3));
// when
String latestProductNumber = productRepository.findLatestProductNumber();
// then
assertThat(latestProductNumber).isEqualTo(targetProductNumber);
}
@DisplayName("가장 마지막 저장한 상품의 상품번호를 읽어올때 상품이 하나도 없는 경우에는 null을 반환한다..")
@Test
void findLatestProductNumberWhenProductIsEmpty() {
// when
String latestProductNumber = productRepository.findLatestProductNumber();
// then
assertThat(latestProductNumber).isNull();
}
상품이 정상적으로 생성되었을때, 상품을 생성할때 상품이 없는경우 두가지 경우 테스트
얕은 수준의 Service테스트는 Repository서비스와 유사하다.
그럼에도 작성을 하는것이 옳다. 조금의 차이가있을수도 있고, ...
@ActiveProfiles("test")
@SpringBootTest
class ProductServiceTest {
@Autowired
private ProductRepository productRepository;
@Autowired
private ProductService productService;
@AfterEach
void tearDown() {
productRepository.deleteAllInBatch();
}
// 동시성 이유?
// 있을수 있는데..?
@DisplayName("신규 상품을 등록한다. 상품번호는 가장 최근 상품의 상품번호에서 1증가한 값이다.")
@Test
void creatProduct() {
// given
Product product = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000);
productRepository.save(product);
ProductCreateRequest request = ProductCreateRequest.builder()
.type(HANDMADE)
.sellingStatus(SELLING)
.name("카푸치노")
.price(5000)
.build();
// when
ProductResponse productResponse = productService.createProduct(request);
// then
assertThat(productResponse)
.extracting("productNumber", "type", "sellingStatus", "name", "price")
.contains("002", HANDMADE, SELLING, "카푸치노", 5000);
List<Product> products = productRepository.findAll();
assertThat(products).hasSize(2)
.extracting("productNumber", "type", "sellingStatus", "name", "price")
.containsExactlyInAnyOrder(
tuple("001", HANDMADE, SELLING, "아메리카노", 4000),
tuple("002", HANDMADE, SELLING, "카푸치노", 5000)
);
}
@DisplayName("신규 상품을 등록할때 상품이 없는 경우 등록하면 상품번호는 001이다.")
@Test
void creatProductWhenProductIsEmpty() {
// given
ProductCreateRequest request = ProductCreateRequest.builder()
.type(HANDMADE)
.sellingStatus(SELLING)
.name("카푸치노")
.price(5000)
.build();
// when
ProductResponse productResponse = productService.createProduct(request);
// then
assertThat(productResponse)
.extracting("productNumber", "type", "sellingStatus", "name", "price")
.contains("001", HANDMADE, SELLING, "카푸치노", 5000);
List<Product> products = productRepository.findAll();
assertThat(products).hasSize(1)
.extracting("productNumber", "type", "sellingStatus", "name", "price")
.contains(
tuple("001", HANDMADE, SELLING, "카푸치노", 5000)
);
}
private Product createProduct(String productNumber, ProductType type,
ProductSellingStatus sellingStatus, String name, int price) {
return Product.builder()
.productNumber(productNumber)
.type(type)
.sellingStatus(sellingStatus)
.name(name)
.price(price)
.build();
}
}
public ProductResponse createProduct(ProductCreateRequest request) {
String nextProductNumber = createNextProductNumber();
Product product = request.toEntity(nextProductNumber);
Product savedProduct = productRepository.save(product);
return ProductResponse.builder()
.id(savedProduct.getId())
.productNumber(nextProductNumber)
.name(request.getName())
.type(request.getType())
.price(request.getPrice())
.sellingStatus(request.getSellingStatus())
.build();
}
private String createNextProductNumber() {
String latestProductNumber = productRepository.findLatestProductNumber();
if (latestProductNumber == null) {
return "001";
}
int latestProductNumberInt = Integer.parseInt(latestProductNumber);
int nextProductNumberInt = latestProductNumberInt + 1;
return String.format("%03d", nextProductNumberInt);
}
public ProductResponse createProduct(ProductCreateRequest request) {
String nextProductNumber = createNextProductNumber();
Product product = request.toEntity(nextProductNumber);
Product savedProduct = productRepository.save(product);
return ProductResponse.of(savedProduct);
}
단위 테스트도 Red - Green - Blue 원칙을 지켜서 작성한다.
Red: 상품을 추가할때, 상품이 없는데 추가할때 경우의 테스트 코드를 작성한다.
Green: 비지니스 로직을 구현하고 Test 돌려서 검증한다.
Blue: Builder를 of 매서드를 이용하도록 리펙토링을 진행하고 Test를 진행해 검증한다.