step3) 1편 DDD 세레나데 테스트를 통한 코드 보호
해당 Step에서 레거시 코드를 바탕으로 테스트코드를 작성하는 것을 연습한다.
해당 Step을 하면서 느낀 점은 기존의 코드를 변경하지 않고 테스트코드를 작성하니, 테스트코드에 비즈니스 로직이 많이 들어간다.
나의 생각은 좋은 테스트 코드는 최대한 테스트코드에서 비즈니스 로직을 작성하지 않는 테스트코드가 좋은 코드라는 생각이다.
물론 Mockito를 최대한 사용하지 않는 것도 좋은 테스트코드이다.
해당 Step을 하면서 테스트코드에서 객체를 생성 할 때, 생성자를 만든다면 해당 코드는 레거시 코드를 수정하는 행위이기에
TestFixture 사용하여 객체를 생성하는 것을 구현하였다.
이제 부터 본격적으로 내가 작성한 코드를 보여드리겠습니다.
MenuGroupRestControllerTest.java
package kitchenpos.ui;
import com.fasterxml.jackson.databind.ObjectMapper;
import kitchenpos.application.MenuGroupService;
import kitchenpos.domain.MenuGroup;
import kitchenpos.fixture.MenuGroupFixture;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import java.util.UUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(MenuGroupRestController.class)
class MenuGroupRestControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private MenuGroupService menuGroupService;
@Test
void 메뉴_그룹_생성_요청이_성공하면_메뉴_그룹정보를_반환한다() throws Exception {
when(menuGroupService.create(any(MenuGroup.class))).thenReturn(MenuGroupFixture.menuGroup(UUID.fromString("cbc75fae-feb0-4bb1-8be2-cb8ce5d8fded"), "한마리메뉴"));
mockMvc.perform(post("/api/menu-groups")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(MenuGroupFixture.menuGroup(UUID.fromString("cbc75fae-feb0-4bb1-8be2-cb8ce5d8fded"), "한마리메뉴"))))
.andDo(print()).andExpect(status().isCreated());
}
@Test
void 메뉴_그룹_전체_조회_요청이_성공하면_그룹_목록을_반환한다() throws Exception {
when(menuGroupService.findAll()).thenReturn(List.of(MenuGroupFixture.menuGroup(UUID.fromString("cbc75fae-feb0-4bb1-8be2-cb8ce5d8fded"), "한마리메뉴")));
mockMvc.perform(get("/api/menu-groups"))
.andDo(print())
.andExpect(status().isOk());
}
}
메뉴 그룹 정보 반환 테스트코드 작성하였다.
when , then 절 통해서 반환 값을 가져왔다.
MenuRestControllerTest.class
package kitchenpos.ui;
import com.fasterxml.jackson.databind.ObjectMapper;
import kitchenpos.application.MenuService;
import kitchenpos.domain.Menu;
import kitchenpos.domain.MenuGroup;
import kitchenpos.domain.MenuProduct;
import kitchenpos.domain.Product;
import kitchenpos.fixture.MenuFixture;
import kitchenpos.fixture.MenuGroupFixture;
import kitchenpos.fixture.MenuProductFixture;
import kitchenpos.fixture.ProductFixture;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(MenuRestController.class)
class MenuRestControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private MenuService menuService;
private UUID menuId;
private UUID menuGroupId;
private UUID productId;
private MenuGroup menuGroup;
private Product product;
private MenuProduct menuProduct;
private Menu menu;
@BeforeEach
void setUp() {
menuId = UUID.fromString("f59b1e1c-b145-440a-aa6f-6095a0e2d63b");
menuGroupId = UUID.fromString("cbc75fae-feb0-4bb1-8be2-cb8ce5d8fded");
productId = UUID.fromString("3b528244-34f7-406b-bb7e-690912f66b10");
menuGroup = MenuGroupFixture.menuGroup(menuGroupId, "한마리메뉴");
product = ProductFixture.product(productId, "후라이드", new BigDecimal(16000));
menuProduct = MenuProductFixture.menuProduct(1L, 1, product);
menu = MenuFixture.menu("후라이드치킨", new BigDecimal(16000), menuGroup, List.of(menuProduct), true);
}
@Test
void 메뉴_생성_요청이_성공하면_메뉴정보를_반환한다() throws Exception {
when(menuService.create(any(Menu.class))).thenReturn(MenuFixture.menu(menuId, "후라이드치킨", new BigDecimal(16000), menuGroup, List.of(menuProduct), true));
mockMvc.perform(post("/api/menus")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(menu))
)
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(menuId.toString()));
}
@Test
void 메뉴_가격_변경_요청이_성공하면_가격이_변경된다() throws Exception {
when(menuService.changePrice(eq(menuId), any(Menu.class))).thenReturn(MenuFixture.menu(menuId, "후라이드치킨", new BigDecimal(18000), menuGroup, List.of(menuProduct), true));
mockMvc.perform(put("/api/menus/{menuId}/price", menuId)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(menu))
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(menuId.toString()));
}
@Test
void 메뉴_숨김에서_표시_변경_요청이_성공하면_상태가_변경된다() throws Exception {
when(menuService.display(menuId)).thenReturn(MenuFixture.menu(menuId, "후라이드치킨", new BigDecimal(18000), menuGroup, List.of(menuProduct), true));
mockMvc.perform(put("/api/menus/{menuId}/display", menuId)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(MenuFixture.menu(menuId, "후라이드치킨", new BigDecimal(18000), menuGroup, List.of(menuProduct), true)))
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(menuId.toString()))
.andExpect(jsonPath("$.displayed").value(true));
}
@Test
void 메뉴_표시에서_숨김_변경_요청이_성공하면_상태가_변경된다() throws Exception {
when(menuService.hide(menuId)).thenReturn(MenuFixture.menu(menuId, "후라이드치킨", new BigDecimal(18000), menuGroup, List.of(menuProduct), false));
mockMvc.perform(put("/api/menus/{menuId}/hide", menuId)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(MenuFixture.menu(menuId, "후라이드치킨", new BigDecimal(18000), menuGroup, List.of(menuProduct), false)))
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.displayed").value(false));
}
@Test
void 메뉴_전체_조회_요청이_성공하면_메뉴_목록을_반환한다() throws Exception {
final Menu findMenu = MenuFixture.menu(menuId, "후라이드치킨", new BigDecimal(18000), menuGroup, List.of(menuProduct), true);
when(menuService.findAll()).thenReturn(List.of(findMenu));
mockMvc.perform(get("/api/menus"))
.andDo(print())
.andExpect(status().isOk());
}
}
메뉴에 대한 테스트 컨트롤러 작성해주었다.
실패 테스트도 작성할까 고민하였지만, 이미 실패테스트는 ServiceTest에서 구현해주었기 때문에 굳이라는 생각이 들어서 실패지점 테스트 코드는 빼고 작성해주었다.
id, displayed 통해서, 검증했다.
OrderRestControllerTest.class
package kitchenpos.ui;
import com.fasterxml.jackson.databind.ObjectMapper;
import kitchenpos.application.OrderService;
import kitchenpos.domain.Order;
import kitchenpos.domain.OrderStatus;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static kitchenpos.fixture.OrderFixture.order;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(OrderRestController.class)
class OrderRestControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private OrderService orderService;
@Test
void 주문_생성_요청이_성공하면_주문정보를_반환한다() throws Exception {
final Order order = order();
when(orderService.create(any(Order.class))).thenReturn(order);
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(order(null, order.getType(), order.getStatus(), order.getOrderDateTime(), order.getOrderLineItems(), order.getDeliveryAddress(), order.getOrderTable(), order.getOrderTableId())))
)
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(order.getId().toString()));
}
@Test
void 주문_승인_요청이_성공하면_상태가_변경된다() throws Exception {
final Order order = order();
when(orderService.accept(order.getId())).thenReturn(order(order.getId(), order.getType(), OrderStatus.ACCEPTED, order.getOrderDateTime(), order.getOrderLineItems(), order.getDeliveryAddress(), order.getOrderTable(), order.getOrderTableId()));
mockMvc.perform(put("/api/orders/{orderId}/accept", order.getId())
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(OrderStatus.ACCEPTED.name()));
}
@Test
void 주문_서빙_요청이_성공하면_상태가_변경된다() throws Exception {
final Order order = order();
when(orderService.serve(order.getId())).thenReturn(order(order.getId(), order.getType(), OrderStatus.SERVED, order.getOrderDateTime(), order.getOrderLineItems(), order.getDeliveryAddress(), order.getOrderTable(), order.getOrderTableId()));
mockMvc.perform(put("/api/orders/{orderId}/serve", order.getId())
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(OrderStatus.SERVED.name()));
}
@Test
void 주문_배달_시작_요청이_성공하면_배달_상태가_변경된다() throws Exception {
final Order order = order();
when(orderService.startDelivery(order.getId())).thenReturn(order(order.getId(), order.getType(), OrderStatus.DELIVERING, order.getOrderDateTime(), order.getOrderLineItems(), order.getDeliveryAddress(), order.getOrderTable(), order.getOrderTableId()));
mockMvc.perform(put("/api/orders/{orderId}/start-delivery", order.getId())
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(OrderStatus.DELIVERING.name()));
}
@Test
void 주문_배달됨_요청이_성공하면_상태가_변경된다() throws Exception {
final Order order = order();
when(orderService.completeDelivery(order.getId())).thenReturn(order(order.getId(), order.getType(), OrderStatus.DELIVERED, order.getOrderDateTime(), order.getOrderLineItems(), order.getDeliveryAddress(), order.getOrderTable(), order.getOrderTableId()));
mockMvc.perform(put("/api/orders/{orderId}/complete-delivery", order.getId())
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(OrderStatus.DELIVERED.name()));
}
@Test
void 주문_배달_완료_요청이_성공하면_상태가_변경된다() throws Exception {
final Order order = order();
when(orderService.complete(order.getId())).thenReturn(order(order.getId(), order.getType(), OrderStatus.COMPLETED, order.getOrderDateTime(), order.getOrderLineItems(), order.getDeliveryAddress(), order.getOrderTable(), order.getOrderTableId()));
mockMvc.perform(put("/api/orders/{orderId}/complete", order.getId())
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(OrderStatus.COMPLETED.name()));
}
@Test
void 주문_조회_요청이_성공하면_주문정보를_반환한다() throws Exception {
when(orderService.findAll()).thenReturn(List.of(order()));
mockMvc.perform(get("/api/orders"))
.andDo(print())
.andExpect(status().isOk());
}
}
Order에 따른 상태 변경에 해당하는 jsonPath("$.status")에 따른 검증을 해주었다.
OrderTableRestControllerTest.class
package kitchenpos.ui;
import com.fasterxml.jackson.databind.ObjectMapper;
import kitchenpos.application.OrderTableService;
import kitchenpos.domain.OrderTable;
import kitchenpos.fixture.OrderTableFixture;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import java.util.UUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(OrderTableRestController.class)
class OrderTableRestControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private OrderTableService orderTableService;
@Test
void 주문_테이블_생성_요청이_성공하면_테이블이_생성된다() throws Exception {
final UUID tableId = UUID.fromString("6ab59e81-06eb-4416-84e9-9faabc87c9ca");
when(orderTableService.create(any(OrderTable.class))).thenReturn(OrderTableFixture.orderTable(tableId, "8번", 0, false));
mockMvc.perform(post("/api/order-tables")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(OrderTableFixture.orderTable(null, "8번", 0, false)))
)
.andDo(print())
.andExpect(status().isCreated());
}
@Test
void 주문_테이블에_앉는_요청을_처리한다() throws Exception {
final UUID tableId = UUID.fromString("6ab59e81-06eb-4416-84e9-9faabc87c9ca");
when(orderTableService.sit(tableId)).thenReturn(OrderTableFixture.orderTable(tableId, "8번", 1, true));
mockMvc.perform(put("/api/order-tables/{orderTableId}/sit", tableId))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(tableId.toString()))
.andExpect(jsonPath("$.name").value("8번"))
.andExpect(jsonPath("$.numberOfGuests").value(1))
.andExpect(jsonPath("$.occupied").value(true));
}
@Test
void 주문_테이블_비우기_요청이_성공하면_테이블이_초기화된다() throws Exception {
final UUID tableId = UUID.fromString("6ab59e81-06eb-4416-84e9-9faabc87c9ca");
when(orderTableService.clear(tableId)).thenReturn(OrderTableFixture.orderTable(tableId, "8번", 0, false));
mockMvc.perform(put("/api/order-tables/{orderTableId}/clear", tableId)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(OrderTableFixture.orderTable(tableId, "8번", 9, true)))
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(tableId.toString()))
.andExpect(jsonPath("$.name").value("8번"))
.andExpect(jsonPath("$.numberOfGuests").value(0))
.andExpect(jsonPath("$.occupied").value(false));
}
@Test
void 주문_테이블_게스트_숫자_변경_요청이_성공하면_숫자가_변경된다() throws Exception {
final UUID tableId = UUID.fromString("6ab59e81-06eb-4416-84e9-9faabc87c9ca");
when(orderTableService.changeNumberOfGuests(eq(tableId), any(OrderTable.class))).thenReturn(OrderTableFixture.orderTable(tableId, "8번", 5, true));
mockMvc.perform(put("/api/order-tables/{orderTableId}/number-of-guests", tableId)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(OrderTableFixture.orderTable(tableId, "8번", 3, true)))
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("8번"))
.andExpect(jsonPath("$.id").value(tableId.toString()))
.andExpect(jsonPath("$.numberOfGuests").value(5))
.andExpect(jsonPath("$.occupied").value(true));
}
@Test
void 주문_테이블_전체_조회_요청이_성공하면_테이블_목록을_반환한다() throws Exception {
final UUID tableId = UUID.fromString("6ab59e81-06eb-4416-84e9-9faabc87c9ca");
when(orderTableService.findAll()).thenReturn(List.of(OrderTableFixture.orderTable(tableId, "8번", 5, true)));
mockMvc.perform(get("/api/order-tables"))
.andDo(print())
.andExpect(status().isOk());
}
}
jsonPath("$.name") , jsonPath("$.id") , jsonPath("$.numberOfGuests") , jsonPath("$.occupied")
4개에 따른 값들에 대한 검증들을 확인할 수 있다.
ProductRestControllerTest.class
package kitchenpos.ui;
import com.fasterxml.jackson.databind.ObjectMapper;
import kitchenpos.application.ProductService;
import kitchenpos.domain.Product;
import kitchenpos.fixture.ProductFixture;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(ProductRestController.class)
class ProductRestControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private ProductService productService;
@Test
void 상품_생성_요청이_성공하면_생성된_상품정보를_반환한다() throws Exception {
final UUID productId = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
when(productService.create(any(Product.class))).thenReturn(ProductFixture.product(productId, "테스트 상품", new BigDecimal(10000)));
mockMvc.perform(post("/api/products")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(ProductFixture.product(null, "테스트 상품", new BigDecimal(10000))))
)
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(productId.toString()))
.andExpect(jsonPath("$.name").value("테스트 상품"))
.andExpect(jsonPath("$.price").value(10000));
}
@Test
void 상품_가격_변경_요청이_성공하면_변경된_상품정보를_반환한다() throws Exception {
final UUID productId = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
when(productService.changePrice(eq(productId), any(Product.class))).thenReturn(ProductFixture.product(productId, "테스트 상품", new BigDecimal("18000")));
mockMvc.perform(put("/api/products/" + productId + "/price")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(ProductFixture.product(productId, "테스트 상품", new BigDecimal("18000"))))
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(productId.toString()))
.andExpect(jsonPath("$.name").value("테스트 상품"))
.andExpect(jsonPath("$.price").value(new BigDecimal("18000")));
}
@Test
void 상품_전체_찾기_요청이_성공하면_상품목록을_반환한다() throws Exception {
final Product product = ProductFixture.product(UUID.fromString("550e8400-e29b-41d4-a716-446655440000"), "테스트 상품", new BigDecimal(15000));
when(productService.findAll()).thenReturn(List.of(product));
mockMvc.perform(get("/api/products")
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
}
}
jsonPath("$.id") , jsonPath("$.name"), jsonPath("$.price")
MenuGroupServiceTest.class
package kitchenpos.application;
import kitchenpos.domain.MenuGroup;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import static kitchenpos.fixture.MenuGroupFixture.DEFAULT_MENU_GROUP_NAME;
import static kitchenpos.fixture.MenuGroupFixture.menuGroup;
import static org.assertj.core.api.Assertions.*;
@Transactional
@SpringBootTest
class MenuGroupServiceTest {
@Autowired
private MenuGroupService menuGroupService;
@Test
void 유효한_이름으로_메뉴_그룹을_생성하면_메뉴_그룹이_정상적으로_생성된다() {
// given
final MenuGroup request = menuGroup(null, DEFAULT_MENU_GROUP_NAME);
// when
final MenuGroup response = menuGroupService.create(request);
// then
assertThat(response.getName()).isEqualTo(request.getName());
}
@Test
void 메뉴_그룹_생성_시_이름이_NULL이면_예외가_발생한다() {
// given
final MenuGroup request = menuGroup(null, null);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuGroupService.create(request));
}
@Test
void 메뉴_그룹_생성_시_이름이_빈_문자열이면_예외가_발생한다() {
// given
final MenuGroup request = menuGroup(null, "");
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuGroupService.create(request));
}
@Test
void 전체_메뉴_그룹_조회_시_생성된_모든_메뉴_그룹이_반환된다() {
// given
final MenuGroup request1 = menuGroup(null, DEFAULT_MENU_GROUP_NAME);
menuGroupService.create(request1);
final MenuGroup request2 = menuGroup(null, "피자 메뉴");
menuGroupService.create(request2);
// when
List<MenuGroup> response = menuGroupService.findAll();
// then
assertThat(response).hasSize(2);
}
}
@Test
void 유효한_이름으로_메뉴_그룹을_생성하면_메뉴_그룹이_정상적으로_생성된다() {
// given
final MenuGroup request = menuGroup(null, DEFAULT_MENU_GROUP_NAME);
// when
final MenuGroup response = menuGroupService.create(request);
// then
assertThat(response.getName()).isEqualTo(request.getName());
}
- menuGroupService.create(request) 호출하면 메뉴 그룹이 생성되어야 함
- 생성된 메뉴 그룹의 name 값이 요청 값과 동일한지 검증
@Test
void 메뉴_그룹_생성_시_이름이_NULL이면_예외가_발생한다() {
// given
final MenuGroup request = menuGroup(null, null);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuGroupService.create(request));
}
- 이름이 null인 경우 예외 발생을 검증
- menuGroupService.create(request) 실행 시 IllegalArgumentException 예외가 발생해야 성공
@Test
void 메뉴_그룹_생성_시_이름이_빈_문자열이면_예외가_발생한다() {
// given
final MenuGroup request = menuGroup(null, "");
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuGroupService.create(request));
}
- 이름이 빈 문자열 ""이면 예외 발생을 검증
- IllegalArgumentException 예외가 발생하는지 확인
@Test
void 전체_메뉴_그룹_조회_시_생성된_모든_메뉴_그룹이_반환된다() {
// given (두 개의 메뉴 그룹을 저장)
final MenuGroup request1 = menuGroup(null, DEFAULT_MENU_GROUP_NAME);
menuGroupService.create(request1);
final MenuGroup request2 = menuGroup(null, "피자 메뉴");
menuGroupService.create(request2);
// when (모든 메뉴 그룹 조회)
List<MenuGroup> response = menuGroupService.findAll();
// then (검증)
assertThat(response).hasSize(2);
}
- 두 개의 MenuGroup 생성 후 menuGroupService.findAll() 호출
- 반환된 리스트 크기가 2개인지 검증 (assertThat(response).hasSize(2))
MenuServiceTest.class
package kitchenpos.application;
import kitchenpos.domain.*;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import static kitchenpos.fixture.MenuFixture.*;
import static kitchenpos.fixture.MenuGroupFixture.menuGroup;
import static kitchenpos.fixture.MenuProductFixture.*;
import static kitchenpos.fixture.ProductFixture.product;
import static org.assertj.core.api.Assertions.*;
@Transactional
@SpringBootTest
class MenuServiceTest {
@Autowired
private MenuService menuService;
@Autowired
private MenuGroupRepository menuGroupRepository;
@Autowired
private ProductRepository productRepository;
@Autowired
private MenuRepository menuRepository;
@Nested
class 메뉴_생성_경우 {
@Test
void 유효한_메뉴_요청이면_정상적으로_생성된다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED);
// when
final Menu response = menuService.create(request);
// then
assertThat(response.getName()).isEqualTo(request.getName());
assertThat(response.getPrice()).isEqualTo(request.getPrice());
assertThat(response.getMenuGroup().getId()).isEqualTo(request.getMenuGroupId());
assertThat(response.getMenuProducts()).hasSize(1);
}
@Test
void 메뉴_가격이_구성_상품_총_합보다_높으면_예외가_발생한다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, 12_000L, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
@Test
void 이름이_NULL이면_예외가_발생한다() {
// given
final Menu request = menu(null, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
@ParameterizedTest(name = "입력 값 `{0}`")
@ValueSource(strings = {"bitch", "shit"})
void 이름의_욕설이_포함되면_예외가_발생한다(final String name) {
// given
final Menu request = menu(name, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
@Test
void 메뉴_상품이_NULL이면_예외가_발생한다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), null, DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
@Test
void 메뉴_상품이_비어있다면_예외가_발생한다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), Collections.emptyList(), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
@Test
void 구성_상품의_수량이_음수이면_예외가_발생한다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(-1L)), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
}
@Nested
class 메뉴_표시_경우 {
@Test
void 구성_상품_총_합이_메뉴_가격_이상이면_메뉴를_표시할_수_있다() {
// given
final Menu request = menuService.create(menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED));
// when
final Menu response = menuService.display(request.getId());
// then
assertThat(response.isDisplayed()).isTrue();
}
@Test
void 구성_상품_총_합이_메뉴_가격보다_낮으면_메뉴_표시_시_예외가_발생한다() {
// given
final Menu request = menuRepository.save(menu(createMenuId(), DEFAULT_MENU_NAME, BigDecimal.valueOf(12_000L), createMenuGroup(), List.of(createMenuProduct(1L)), false));
// when & then
assertThatIllegalStateException()
.isThrownBy(() -> menuService.display(request.getId()));
}
}
@Nested
class 메뉴_숨김_경우 {
@Test
void 메뉴_숨김_처리는_정상적으로_수행된다() {
// given
final Menu request = menuService.create(menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED));
// when
final Menu response = menuService.hide(request.getId());
// then
assertThat(response.isDisplayed()).isFalse();
}
}
@Nested
class 메뉴_조회_경우 {
@Test
void 전체_메뉴를_조회하면_생성된_모든_메뉴가_반환된다() {
// given
final Product product1 = product("후라이드 치킨", 14_000);
final Product product2 = product("양념 치킨", 14_000);
productRepository.saveAll(List.of(product1, product2, product2));
final MenuProduct menuProduct1 = menuProduct(seq(), DEFALUT_QUANTITY, product1);
final MenuProduct menuProduct2 = menuProduct(seq(), DEFALUT_QUANTITY, product2);
final Menu request1 = menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED);
menuService.create(request1);
final Menu request2 = menu(SECONDARY_MENU_NAME, SECONDARY_MENU_PRICE, createMenuGroup(), List.of(menuProduct1, menuProduct2), DEFAULT_DISPLAYED);
menuService.create(request2);
// when
List<Menu> response = menuService.findAll();
// then
assertThat(response).hasSize(2);
}
}
private MenuGroup createMenuGroup() {
return menuGroupRepository.save(menuGroup());
}
private MenuProduct createMenuProduct(final long quantity) {
return menuProduct(seq(), quantity, productRepository.save(product()));
}
}
@Nested : 테스트를 기능별로 그룹화하여 가독성을 높이고, 특정 기능만 실행할 수 있도록 분리
1. 메뉴 생성 테스트 (@Nested class 메뉴_생성_경우)
@Test
void 유효한_메뉴_요청이면_정상적으로_생성된다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED);
// when
final Menu response = menuService.create(request);
// then
assertThat(response.getName()).isEqualTo(request.getName());
assertThat(response.getPrice()).isEqualTo(request.getPrice());
assertThat(response.getMenuGroup().getId()).isEqualTo(request.getMenuGroupId());
assertThat(response.getMenuProducts()).hasSize(1);
}
- 유효한 Menu 생성했을 때 정상적으로 생성되는지 검증
- name, price, menuGroupId, menuProducts 요청과 동일한지 확인
메뉴 가격이 구성 상품의 총합보다 높으면 예외 발생
@Test
void 메뉴_가격이_구성_상품_총_합보다_높으면_예외가_발생한다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, 12_000L, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
- 메뉴 가격이 상품 가격 총합보다 높으면 예외가 발생해야 함.
이름이 null이면 예외 발생
@Test
void 메뉴_가격이_구성_상품_총_합보다_높으면_예외가_발생한다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, 12_000L, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
- 메뉴의 name이 null이면 예외 발생
욕설이 포함된 이름이면 예외 발생(@ParameterizedTest)
@ParameterizedTest(name = "입력 값 `{0}`")
@ValueSource(strings = {"bitch", "shit"})
void 이름의_욕설이_포함되면_예외가_발생한다(final String name) {
// given
final Menu request = menu(name, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
- 금지된 단어(bitch, shit)가 포함되면 예외가 발생해야 함
구성 상품이 null이거나 비어있으면 예외발생
@Test
void 메뉴_상품이_NULL이면_예외가_발생한다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), null, DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
@Test
void 메뉴_상품이_비어있다면_예외가_발생한다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), Collections.emptyList(), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
- 구성 상품이 null이거나 비어있으면 메뉴를 생성할 수 없음
구성 상품의 수량이 음수이면 예외 발생
@Test
void 구성_상품의_수량이_음수이면_예외가_발생한다() {
// given
final Menu request = menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(-1L)), DEFAULT_DISPLAYED);
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> menuService.create(request));
}
- 구성 상품의 수량이 음수인 경우 예외 발생
메뉴 표시 테스트 (@Nested class 메뉴_표시_경우)
@Test
void 구성_상품_총_합이_메뉴_가격_이상이면_메뉴를_표시할_수_있다() {
// given
final Menu request = menuService.create(menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED));
// when
final Menu response = menuService.display(request.getId());
// then
assertThat(response.isDisplayed()).isTrue();
}
- 구성 상품 총합 >= 메뉴 가격 -> 메뉴를 정상적으로 표시 가능
구성 상품 총합 < 메뉴 가격이면 메뉴 표시 시 예외 발생
@Test
void 구성_상품_총_합이_메뉴_가격보다_낮으면_메뉴_표시_시_예외가_발생한다() {
// given
final Menu request = menuRepository.save(menu(createMenuId(), DEFAULT_MENU_NAME, BigDecimal.valueOf(12_000L), createMenuGroup(), List.of(createMenuProduct(1L)), false));
// when & then
assertThatIllegalStateException()
.isThrownBy(() -> menuService.display(request.getId()));
}
- 상품 가격 총합이 메뉴 가격보다 낮으면 메뉴를 표시할 수 없음
메뉴 숨김 테스트 (@Nested class 메뉴_숨김_경우)
@Test
void 메뉴_숨김_처리는_정상적으로_수행된다() {
// given
final Menu request = menuService.create(menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED));
// when
final Menu response = menuService.hide(request.getId());
// then
assertThat(response.isDisplayed()).isFalse();
}
- 메뉴 숨김이 정상적으로 수행되는지 검증
메뉴 조회 테스트 (@Nested class 메뉴_조회_경우)
@Test
void 전체_메뉴를_조회하면_생성된_모든_메뉴가_반환된다() {
// given
menuService.create(menu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE, createMenuGroup(), List.of(createMenuProduct(1L)), DEFAULT_DISPLAYED));
menuService.create(menu(SECONDARY_MENU_NAME, SECONDARY_MENU_PRICE, createMenuGroup(), List.of(menuProduct1, menuProduct2), DEFAULT_DISPLAYED));
// when
List<Menu> response = menuService.findAll();
// then
assertThat(response).hasSize(2);
}
- findAll() 호출 시 생성된 메뉴들이 모두 반환되는지 확인
OrderServiceTest.class
package kitchenpos.application;
import kitchenpos.domain.*;
import kitchenpos.infra.KitchenridersClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import static kitchenpos.fixture.MenuFixture.*;
import static kitchenpos.fixture.MenuGroupFixture.menuGroup;
import static kitchenpos.fixture.MenuProductFixture.*;
import static kitchenpos.fixture.OrderFixture.DEFAULT_DELIVERY_ADDRESS;
import static kitchenpos.fixture.OrderFixture.order;
import static kitchenpos.fixture.OrderTableFixture.DEFAULT_ORDER_TABLE_NAME;
import static kitchenpos.fixture.OrderTableFixture.orderTable;
import static kitchenpos.fixture.ProductFixture.product;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@Transactional
@SpringBootTest
class OrderServiceTest {
@Autowired
private OrderService orderService;
@Autowired
private MenuRepository menuRepository;
@Autowired
private ProductRepository productRepository;
@Autowired
private MenuGroupRepository menuGroupRepository;
@Autowired
private OrderTableRepository orderTableRepository;
@MockBean
private KitchenridersClient kitchenridersClient;
private Menu menu;
@BeforeEach
void setUp() {
menu = createMenu(DEFAULT_MENU_NAME, DEFAULT_MENU_PRICE);
}
@Nested
class 주문_생성_경우 {
@Test
void 포장_주문은_유효한_주문_라인_아이템이_주어지면_정상적으로_생성된다() {
// given
final Order request = order(OrderType.TAKEOUT, menu);
// when
final Order response = orderService.create(request);
// then
assertThat(response.getType()).isEqualTo(request.getType());
assertThat(response.getStatus()).isEqualTo(OrderStatus.WAITING);
assertThat(response.getOrderLineItems()).hasSize(1);
}
@Test
void 배달_주문은_유효한_배송_주소가_주어지면_정상적으로_생성된다() {
final Order request = order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS);
// when
final Order response = orderService.create(request);
// then
assertThat(response.getType()).isEqualTo(request.getType());
assertThat(response.getDeliveryAddress()).isEqualTo(request.getDeliveryAddress());
assertThat(response.getOrderLineItems()).hasSize(1);
}
@Test
void 매장_식사_주문은_착석된_주문_테이블이_주어지면_정상적으로_생성된다() {
// given
final Order request = order(OrderType.EAT_IN, menu, createOrderTable(4));
// when
final Order response = orderService.create(request);
// then
assertThat(response.getType()).isEqualTo(request.getType());
assertThat(response.getOrderTable().getId()).isEqualTo(request.getOrderTableId());
}
@Test
void 주문_생성_시_필수_필드가_누락되면_예외가_발생한다() {
// given
final Order request = new Order();
request.setOrderLineItems(new ArrayList<>());
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> orderService.create(request));
}
@Test
void 배달_주문_생성_시_배송_주소가_없으면_예외가_발생한다() {
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> orderService.create(order(OrderType.DELIVERY, menu, "")));
}
}
@Nested
class 주문_상태_전환_경우 {
@Test
void WAITING_상태의_DELIVERY_주문은_accept를_통해_ACCEPTED_상태로_전환되고_배달_요청이_진행된다() {
// given
final Order request = orderService.create(order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS));
// when
final Order response = orderService.accept(request.getId());
// then
assertThat(response.getStatus()).isEqualTo(OrderStatus.ACCEPTED);
verify(kitchenridersClient, times(1))
.requestDelivery(eq(request.getId()), any(BigDecimal.class), eq(DEFAULT_DELIVERY_ADDRESS));
}
@Test
void WAITING_상태가_아닌_주문은_accept_시_예외가_발생한다() {
// given
final Order created = orderService.create(order(OrderType.TAKEOUT, menu));
created.setStatus(OrderStatus.ACCEPTED);
// when & then
assertThatIllegalStateException()
.isThrownBy(() -> orderService.accept(created.getId()));
}
@Test
void ACCEPTED_상태의_주문은_serve를_통해_SERVED_상태로_전환된다() {
// given
final Order request = orderService.create(order(OrderType.TAKEOUT, menu));
request.setStatus(OrderStatus.ACCEPTED);
// when
final Order response = orderService.serve(request.getId());
// then
assertThat(response.getStatus()).isEqualTo(OrderStatus.SERVED);
}
@Test
void SERVED_상태의_TAKEOUT_주문은_startDelivery_시_예외가_발생한다() {
// given
final Order created = orderService.create(order(OrderType.TAKEOUT, menu));
created.setStatus(OrderStatus.SERVED);
// when & then
assertThatIllegalStateException()
.isThrownBy(() -> orderService.startDelivery(created.getId()));
}
@Test
void SERVED_상태의_DELIVERY_주문은_startDelivery를_통해_DELIVERING_상태로_전환된다() {
// given
final Order request = orderService.create(order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS));
request.setStatus(OrderStatus.SERVED);
// when
final Order response = orderService.startDelivery(request.getId());
// then
assertThat(response.getStatus()).isEqualTo(OrderStatus.DELIVERING);
}
@Test
void 배달_중인_상태의_주문은_배달완료_상태로_전환된다() {
// given
final Order request = orderService.create(order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS));
request.setStatus(OrderStatus.DELIVERING);
// when
final Order response = orderService.completeDelivery(request.getId());
// then
assertThat(response.getStatus()).isEqualTo(OrderStatus.DELIVERED);
}
@Test
void DELIVERY_주문_완료_시_상태_조건을_만족하지_않으면_예외가_발생한다() {
// given
final Order createdDelivery = orderService.create(order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS));
// when & then
assertThatIllegalStateException()
.isThrownBy(() -> orderService.complete(createdDelivery.getId()));
}
@Test
void TAKEOUT_주문_완료_시_상태_조건을_만족하지_않으면_예외가_발생한다() {
// given
final Order request = orderService.create(order(OrderType.TAKEOUT, menu));
// when & then
assertThatIllegalStateException()
.isThrownBy(() -> orderService.complete(request.getId()));
}
@Test
void DELIVERY_주문은_DELIVERED_상태여야_완료되며_완료되면_COMPLETED_상태로_전환된다() {
// given
final Order request = orderService.create(order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS));
request.setStatus(OrderStatus.DELIVERED);
// when
final Order response = orderService.complete(request.getId());
// then
assertThat(response.getStatus()).isEqualTo(OrderStatus.COMPLETED);
}
@Test
void TAKEOUT_주문은_SERVED_상태여야_완료되며_완료되면_COMPLETED_상태로_전환된다() {
// given
final Order request = orderService.create(order(OrderType.TAKEOUT, menu));
request.setStatus(OrderStatus.SERVED);
// when
final Order response = orderService.complete(request.getId());
// then
assertThat(response.getStatus()).isEqualTo(OrderStatus.COMPLETED);
}
@Test
void EAT_IN_주문은_SERVED_상태여야_완료되며_완료되면_COMPLETED_상태로_전환되고_테이블이_초기화된다() {
// given
final Order request = orderService.create(order(OrderType.EAT_IN, menu, createOrderTable(4)));
request.setStatus(OrderStatus.SERVED);
// when
final Order response1 = orderService.complete(request.getId());
final OrderTable response2 = orderTableRepository.findById(response1.getOrderTable().getId()).orElseThrow(NoSuchElementException::new);
// then
assertThat(response1.getStatus()).isEqualTo(OrderStatus.COMPLETED);
assertThat(response2.getNumberOfGuests()).isZero();
assertThat(response2.isOccupied()).isFalse();
}
}
@Nested
class 주문_조회_경우 {
@Test
void 전체_주문을_조회하면_생성된_모든_주문이_반환된다() {
// given
orderService.create(order(OrderType.TAKEOUT, menu));
orderService.create(order(OrderType.TAKEOUT, menu));
// when
List<Order> response = orderService.findAll();
// then
assertThat(response).hasSize(2);
}
}
private Menu createMenu(final String name, final BigDecimal price) {
final MenuGroup menuGroup = menuGroup();
menuGroupRepository.save(menuGroup);
final Product product = product();
productRepository.save(product);
final MenuProduct menuProduct = menuProduct(seq(), DEFALUT_QUANTITY, product);
return menuRepository.save(menu(createMenuId(), name, price, menuGroup, List.of(menuProduct), DEFAULT_DISPLAYED));
}
private OrderTable createOrderTable(final int numberOfGuests) {
return orderTableRepository.save(orderTable(DEFAULT_ORDER_TABLE_NAME, numberOfGuests));
}
}
1. 주문 생성 테스트 (@Nested class 주문_생성_경우)
포장(TAKEOUT) 주문이 정상적으로 생성되는 경우
@Test
void 포장_주문은_유효한_주문_라인_아이템이_주어지면_정상적으로_생성된다() {
// given
final Order request = order(OrderType.TAKEOUT, menu);
// when
final Order response = orderService.create(request);
// then
assertThat(response.getType()).isEqualTo(request.getType());
assertThat(response.getStatus()).isEqualTo(OrderStatus.WAITING);
assertThat(response.getOrderLineItems()).hasSize(1);
}
- 유효한 주문이 주어지면 정상적으로 생성되어야 한다.
- 주문 타입(OrderType.TAKEOUT)과 상태(OrderStatus.WAITING)을 확인
2. 배달(DELIVERY) 주문이 정상적으로 생성되는 경우
@Test
void 배달_주문은_유효한_배송_주소가_주어지면_정상적으로_생성된다() {
final Order request = order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS);
// when
final Order response = orderService.create(request);
// then
assertThat(response.getType()).isEqualTo(request.getType());
assertThat(response.getDeliveryAddress()).isEqualTo(request.getDeliveryAddress());
assertThat(response.getOrderLineItems()).hasSize(1);
}
- 배달 주문 시 배송 주소가 존재하면 정상적으로 생성되어야 함.
3. 주문 생성 시 필수 필드가 누락되면 예외 발생
@Test
void 주문_생성_시_필수_필드가_누락되면_예외가_발생한다() {
// given
final Order request = new Order();
request.setOrderLineItems(new ArrayList<>());
// when & then
assertThatIllegalArgumentException()
.isThrownBy(() -> orderService.create(request));
}
- 주문 항목이 비어 있으면 예외 발생해야 함.
주문 상태 전환 테스트(@Nested class 주문_상태_전환_경우)
@Test
void WAITING_상태의_DELIVERY_주문은_accept를_통해_ACCEPTED_상태로_전환되고_배달_요청이_진행된다() {
// given
final Order request = orderService.create(order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS));
// when
final Order response = orderService.accept(request.getId());
// then
assertThat(response.getStatus()).isEqualTo(OrderStatus.ACCEPTED);
verify(kitchenridersClient, times(1))
.requestDelivery(eq(request.getId()), any(BigDecimal.class), eq(DEFAULT_DELIVERY_ADDRESS));
}
- 배달 주문은 ACCEPTED 상태로 전환되며 배달 요청이 진행됨
- 주문이 WAITING 상태에서 ACCEPTED로 변경되며, KitchenridersClient.requestDelivery()가 호출되는지 검증.
대기 상태가 아닌 주문을 accept()하면 예외 발생
@Test
void WAITING_상태가_아닌_주문은_accept_시_예외가_발생한다() {
// given
final Order created = orderService.create(order(OrderType.TAKEOUT, menu));
created.setStatus(OrderStatus.ACCEPTED);
// when & then
assertThatIllegalStateException()
.isThrownBy(() -> orderService.accept(created.getId()));
}
- 이미 ACCEPTED 상태인 주문을 accept()하면 예외 발생.
포장(TAKEOUT) 주문을 배달 시작하면 예외 발생
@Test
void SERVED_상태의_TAKEOUT_주문은_startDelivery_시_예외가_발생한다() {
// given
final Order created = orderService.create(order(OrderType.TAKEOUT, menu));
created.setStatus(OrderStatus.SERVED);
// when & then
assertThatIllegalStateException()
.isThrownBy(() -> orderService.startDelivery(created.getId()));
}
- 포장 주문은 배달할 수 없으므로 startDelivery()하면 예외 발생해야 함.
배달 중인 주문은 배달 완료 상태로 전환
@Test
void 배달_중인_상태의_주문은_배달완료_상태로_전환된다() {
// given
final Order request = orderService.create(order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS));
request.setStatus(OrderStatus.DELIVERING);
// when
final Order response = orderService.completeDelivery(request.getId());
// then
assertThat(response.getStatus()).isEqualTo(OrderStatus.DELIVERED);
}
- 배달 중(DELIVERING)인 주문이 DELIVERED로 전환되는지 검증.
주문 완료 테스트
@Test
void DELIVERY_주문은_DELIVERED_상태여야_완료되며_완료되면_COMPLETED_상태로_전환된다() {
// given
final Order request = orderService.create(order(OrderType.DELIVERY, menu, DEFAULT_DELIVERY_ADDRESS));
request.setStatus(OrderStatus.DELIVERED);
// when
final Order response = orderService.complete(request.getId());
// then
assertThat(response.getStatus()).isEqualTo(OrderStatus.COMPLETED);
}
- 배달 주문이 DELIVERED 상태일 때만 COMPLETED 상태로 변경됨.
매장(EAT_IN) 주문 완료 시 테이블 초기화
@Test
void EAT_IN_주문은_SERVED_상태여야_완료되며_완료되면_COMPLETED_상태로_전환되고_테이블이_초기화된다() {
// given
final Order request = orderService.create(order(OrderType.EAT_IN, menu, createOrderTable(4)));
request.setStatus(OrderStatus.SERVED);
// when
final Order response1 = orderService.complete(request.getId());
final OrderTable response2 = orderTableRepository.findById(response1.getOrderTable().getId()).orElseThrow(NoSuchElementException::new);
// then
assertThat(response1.getStatus()).isEqualTo(OrderStatus.COMPLETED);
assertThat(response2.getNumberOfGuests()).isZero();
assertThat(response2.isOccupied()).isFalse();
}
https://github.com/hanseu9839/ddd-legacy/tree/step3
GitHub - hanseu9839/ddd-legacy
Contribute to hanseu9839/ddd-legacy development by creating an account on GitHub.
github.com