Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[레거시 코드 리팩터링 - 1단계] 테오(최우성) 미션 제출합니다. #457

Merged
merged 20 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0f8f4b6
docs: 요구 사항 명세
woosung1223 Oct 10, 2023
70894d8
feat: 메뉴 그룹 관련 테스트 구현
woosung1223 Oct 10, 2023
8d94285
feat: cleanup.sql 적용
woosung1223 Oct 10, 2023
60423f5
test: 메뉴 생성 기능 테스트 구현
woosung1223 Oct 10, 2023
1ed40a0
test: 메뉴 조회 기능 테스트 구현
woosung1223 Oct 10, 2023
3d07c9c
chore: 변수명 변경 및 불필요한 `@Autowired` 제거
woosung1223 Oct 10, 2023
bc749bd
test: 상품 생성 및 조회 기능 테스트 구현
woosung1223 Oct 10, 2023
f3d8ff2
test: 주문 생성 기능 테스트 구현
woosung1223 Oct 10, 2023
38ca990
refactor: 테스트 컨텍스트를 공유하도록 변경
woosung1223 Oct 10, 2023
77fe194
chore: 불필요한 생성자 제거
woosung1223 Oct 10, 2023
705985d
test: 주문 조회, 상태 변경 기능 테스트 구현
woosung1223 Oct 10, 2023
dbd0669
chore: 빠진 given 절 추가
woosung1223 Oct 11, 2023
52c18b8
test: 테이블 상태 변경 기능 테스트 구현
woosung1223 Oct 11, 2023
f6a7ef0
test: 테이블 그룹 생성 및 해체 기능 테스트 구현
woosung1223 Oct 11, 2023
dd53f16
refactor: 테스트 코드에서 의미를 보다 명확하게 전달하도록 변경
woosung1223 Oct 11, 2023
4115a1e
docs: 기능 목록 스타일 조정
woosung1223 Oct 11, 2023
505124b
refactor: EOF 추가
woosung1223 Oct 11, 2023
7240487
refactor: 테이블을 어플리케이션 단에서 cleanup 하도록 변경
woosung1223 Oct 11, 2023
fec9582
chore: 메소드를 중복으로 호출하지 않도록 수정
woosung1223 Oct 11, 2023
6994d6c
refactor: 다중 검증을 수행하는 부분은 `SoftAssertions`를 사용하도록 변경
woosung1223 Oct 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 102 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,103 @@
# 키친포스

## 요구 사항
---

## 요구 사항 🚀

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엄청 꼼꼼하게 작성하셨네요 👍

- [x] 메뉴 생성 기능
- [x] 메뉴는 아이디, 이름, 가격, 메뉴 그룹 아이디, 메뉴 상품들의 리스트로 구성된다.
- [x] 메뉴 상품은 일련번호, 메뉴 아이디, 상품 아이디, 개수로 구성된다.
- [x] 메뉴의 가격은 0보다 큰 수여야 한다. (소수점을 포함할 수 있다)
- [x] 메뉴 그룹은 존재하는 메뉴 그룹을 참조해야 한다.
- [x] 메뉴는 고유해야 한다.
- [x] 가격은 실제 메뉴 상품들의 총 가격보다 작아야 한다.
- [x] 메뉴 상품들의 총 가격을 계산하는 방법은 `상품 가격 * 메뉴 상품 개수` 이다.
- [x] 정상적으로 생성되면 `201 CREATED`와 함께 메뉴를 반환한다.


- [x] 메뉴 조회 기능
- [x] 모든 메뉴를 조회한다.
- [x] 정상적으로 조회하면 `200 OK`와 함께 메뉴 리스트를 반환한다.


- [x] 상품 생성 기능
- [x] 상품은 아이디, 이름, 가격으로 구성된다.
- [x] 상품 가격은 0보다 큰 수여야 한다. (소수점을 포함할 수 있다)
- [x] 정상적으로 생성되면 `201 CREATED`와 함께 상품을 반환한다.


- [x] 상품 조회 기능
- [x] 모든 상품을 조회한다.
- [x] 정상적으로 조회하면 `200 OK`와 함께 상품 리스트를 반환한다.


- [x] 주문 생성 기능
- [x] 주문은 아이디, 주문 테이블 아이디, 주문 상태, 주문 시간, 주문 항목 리스트로 구성된다.
- [x] 주문 항목은 일련번호, 주문 아이디, 메뉴 아이디, 수량으로 구성된다.
- [x] 주문 항목은 적어도 하나 이상 존재해야 한다.
- [x] 주문 항목의 메뉴는 모두 존재해야 한다.
- [x] 주문 테이블은 존재해야 한다.
- [x] 주문은 발행되면 주문 상태가 `COOKING`으로 설정된다.
- [x] 정상적으로 생성되면 `201 CREATED`와 함께 주문을 반환한다.


- [x] 주문 조회 기능
- [x] 모든 주문을 조회한다.
- [x] 정상적으로 조회하면 `200 OK`와 함께 주문 리스트를 반환한다.


- [x] 주문 상태 변경 기능
- [x] 해당하는 주문은 존재해야 한다.
- [x] 이미 완료된 주문이면 상태를 변경할 수 없다.
- [x] 정상적으로 변경하면 `200 OK`와 함께 주문을 반환한다.


- [x] 메뉴 그룹 생성 기능
- [x] 메뉴 그룹은 아이디와 이름으로 구성된다.
- [x] 정상적으로 생성되면 `201 CREATED`와 함께 메뉴 그룹을 반환한다.


- [x] 메뉴 그룹 조회 기능
- [x] 모든 메뉴 그룹을 조회한다.
- [x] 정상적으로 조회하면 `200 OK`와 함께 메뉴 그룹 리스트를 반환한다.


- [x] 테이블 생성 기능
- [x] 테이블은 아이디, 테이블 그룹 아이디, 손님 수, 빈 테이블 여부로 구성된다.
- [x] 정상적으로 생성되면 `201 CREATED`와 함께 테이블을 반환한다.


- [x] 테이블 조회 기능
- [x] 모든 테이블을 조회한다.
- [x] 정상적으로 조회하면 `200 OK`와 함께 테이블 리스트를 반환한다.


- [x] 테이블 상태 변경 기능
- [x] 빈 테이블로 변경하는 기능
- [x] 해당하는 테이블은 존재해야 한다.
- [x] 해당 테이블에 테이블 그룹은 있어야 한다.
- [x] 해당 테이블의 주문 상태가 `COOKING`이거나 `MEAL`이면 안된다.
- [x] 정상적으로 변경하면 `200 OK`와 함께 테이블을 반환한다.
- [x] 손님 수 변경 기능
- [x] 손님 수는 0명 이상으로만 변경할 수 있다.
- [x] 테이블은 존재해야 한다.
- [x] 빈 테이블에 대해 손님 수는 변경할 수 없다.
- [x] 정상적으로 변경하면 `200 OK`와 함께 테이블을 반환한다.


- [x] 테이블 그룹 생성 기능
- [x] 테이블 그룹은 아이디, 생성 날짜, 테이블 리스트로 구성된다.
- [x] 테이블은 두 개 이상 지정할 수 있다.
- [x] 테이블은 모두 실제 존재하는 테이블이어야 한다.
- [x] 빈 테이블이면서 그룹에 포함되지 않은 테이블만 그룹으로 지정할 수 있다.
- [x] 정상적으로 생성하면 `201 CREATED`와 함께 테이블 그룹을 반환한다.


- [x] 테이블 그룹 해체 기능
- [x] 해당하는 아이디에 맞는 테이블 그룹을 해체한다.
- [x] 주문 상태가 `COOKING`이거나 `MEAL`이면서 주문이 이미 존재하는 경우 해체할 수 없다.
- [x] 정상적으로 해체한 경우 `204 NO CONTENT`를 반환한다.

---
## 용어 사전

| 한글명 | 영문명 | 설명 |
Expand All @@ -19,3 +115,8 @@
| 단체 지정 | table group | 통합 계산을 위해 개별 주문 테이블을 그룹화하는 기능 |
| 주문 항목 | order line item | 주문에 속하는 수량이 있는 메뉴 |
| 매장 식사 | eat in | 포장하지 않고 매장에서 식사하는 것 |

---
### step1 기록
- 트랜잭션 스크립트 패턴으로 작성된 서비스를 테스트하려니 테스트가 어렵다.
- 모든 서비스 로직이 뭉쳐있기 때문에 기능별로 독립적인 테스트가 어려워진다.
39 changes: 39 additions & 0 deletions src/test/java/kitchenpos/application/MenuGroupServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package kitchenpos.application;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;
import kitchenpos.domain.MenuGroup;
import org.junit.jupiter.api.Test;

@SuppressWarnings("NonAsciiCharacters")
class MenuGroupServiceTest extends ServiceTestContext {

@Test
void 메뉴_그룹을_생성할_수_있다() {
// given
MenuGroup menuGroup = new MenuGroup();
menuGroup.setName("menuGroup");

// when
MenuGroup created = menuGroupService.create(menuGroup);

// then
assertThat(created.getId()).isNotNull();
}

@Test
void 모든_메뉴_그룹을_조회할_수_있다() {
// given
MenuGroup menuGroup = new MenuGroup();
menuGroup.setName("menuGroup");

menuGroupService.create(menuGroup);

// when
List<MenuGroup> menuGroups = menuGroupService.list();

// then
assertThat(menuGroups).hasSize(2);
}
}
93 changes: 93 additions & 0 deletions src/test/java/kitchenpos/application/MenuServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package kitchenpos.application;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.math.BigDecimal;
import java.util.List;
import kitchenpos.domain.Menu;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이미 아시겠지만 이 부분에
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)를 달아주면 DisplayName에 밑줄이 사라진다고 합니다 ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다! 그런데 해당 어노테이션은 ServiceTestContext에 이미 정의해두어서 상속을 받는 테스트 클래스에서는 정의하지 않았습니다! 😄

@SuppressWarnings("NonAsciiCharacters")
class MenuServiceTest extends ServiceTestContext {

@Test
void 메뉴_가격이_0보다_작으면_예외를_던진다() {
// given
Menu menu = new Menu();
menu.setName("menuName");
menu.setPrice(BigDecimal.valueOf(-1));
menu.setMenuGroupId(savedMenuGroup.getId());
menu.setMenuProducts(List.of(savedMenuProduct));

// when, then
assertThatThrownBy(() -> menuService.create(menu))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 메뉴_그룹_아이디에_해당하는_메뉴_그룹이_없는_경우_예외를_던진다() {
// given
Menu menu = new Menu();
menu.setName("menuName");
menu.setPrice(BigDecimal.valueOf(1000L));
menu.setMenuGroupId(Long.MAX_VALUE);
menu.setMenuProducts(List.of(savedMenuProduct));

// when, then
assertThatThrownBy(() -> menuService.create(menu))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 가격이_실제_메뉴_상품들의_총_가격보다_크면_예외를_던진다() {
// given
Menu menu = new Menu();
menu.setName("menuName");
menu.setPrice(BigDecimal.valueOf(2001L));
menu.setMenuGroupId(savedMenuGroup.getId());
menu.setMenuProducts(List.of(savedMenuProduct));

// when, then
assertThatThrownBy(() -> menuService.create(menu))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 메뉴를_정상적으로_생성하는_경우_생성한_메뉴가_반환된다() {
// given
Menu menu = new Menu();
menu.setName("menuName");
menu.setPrice(BigDecimal.valueOf(2000L));
menu.setMenuGroupId(savedMenuGroup.getId());
menu.setMenuProducts(List.of(savedMenuProduct));

// when
Menu createdMenu = menuService.create(menu);

// then
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(createdMenu.getId()).isNotNull();
softly.assertThat(createdMenu.getName()).isEqualTo(menu.getName());
});
}

@Test
void 전체_메뉴를_조회할_수_있다() {
// given
Menu menu = new Menu();
menu.setName("menuName");
menu.setPrice(BigDecimal.valueOf(2000L));
menu.setMenuGroupId(savedMenuGroup.getId());
menu.setMenuProducts(List.of(savedMenuProduct));

menuService.create(menu);

// when
List<Menu> products = menuService.list();

// then
assertThat(products).hasSize(2);
}
}
150 changes: 150 additions & 0 deletions src/test/java/kitchenpos/application/OrderServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package kitchenpos.application;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.util.List;
import kitchenpos.domain.Order;
import kitchenpos.domain.OrderLineItem;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test;

@SuppressWarnings("NonAsciiCharacters")
class OrderServiceTest extends ServiceTestContext {

@Test
void 주문_항목이_없다면_예외를_던진다() {
// given
Order order = new Order();
order.setOrderLineItems(List.of());
order.setOrderTableId(savedOrderTable.getId());

// when, then
assertThatThrownBy(() -> orderService.create(order))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 주문_항목의_메뉴가_존재하지_않는다면_예외를_던진다() {
// given
OrderLineItem orderLineItem = new OrderLineItem();
orderLineItem.setMenuId(Long.MAX_VALUE);
orderLineItem.setQuantity(1L);

Order order = new Order();
order.setOrderLineItems(List.of(orderLineItem));
order.setOrderTableId(savedOrderTable.getId());

// when, then
assertThatThrownBy(() -> orderService.create(order))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 주문_테이블이_존재하지_않는다면_예외를_던진다() {
// given
Order order = new Order();
order.setOrderLineItems(List.of(savedOrderLineItem));
order.setOrderTableId(Long.MAX_VALUE);

// when, then
assertThatThrownBy(() -> orderService.create(order))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 주문은_생성되면_COOKING_상태로_설정된다() {
// given
Order order = new Order();
order.setOrderLineItems(List.of(savedOrderLineItem));
order.setOrderTableId(savedOrderTable.getId());

// when
Order createdOrder = orderService.create(order);

// then
assertThat(createdOrder.getOrderStatus()).isEqualTo("COOKING");
}

@Test
void 주문을_정상적으로_생성하는_경우_생성한_주문이_반환된다() {
// given
Order order = new Order();
order.setOrderLineItems(List.of(savedOrderLineItem));
order.setOrderTableId(savedOrderTable.getId());

// when
Order createdOrder = orderService.create(order);

// then
assertThat(createdOrder.getId()).isNotNull();
}

@Test
void 전체_주문을_조회할_수_있다() {
// given
Order order = new Order();
order.setOrderLineItems(List.of(savedOrderLineItem));
order.setOrderTableId(savedOrderTable.getId());

orderService.create(order);

// when
List<Order> orders = orderService.list();

// then
assertThat(orders).hasSize(2);
}

@Test
void 주문이_존재하지_않으면_상태를_변경하려_할_때_예외를_던진다() {
// given
Long orderId = Long.MAX_VALUE;

Order order = new Order();
order.setOrderLineItems(List.of(savedOrderLineItem));
order.setOrderTableId(savedOrderTable.getId());
order.setOrderStatus("MEAL");

// when, then
assertThatThrownBy(() -> orderService.changeOrderStatus(orderId, order))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 이미_완료된_주문이라면_상태를_변경할_수_없다() {
// given
Long orderId = savedOrder.getId();

Order order = new Order();
order.setOrderLineItems(List.of(savedOrderLineItem));
order.setOrderTableId(savedOrderTable.getId());
order.setOrderStatus("COMPLETION");

orderService.changeOrderStatus(orderId, order);

// when, then
assertThatThrownBy(() -> orderService.changeOrderStatus(orderId, order))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 주문을_정상적으로_변경하는_경우_변경한_주문이_반환된다() {
// given
Long orderId = savedOrder.getId();

Order order = new Order();
order.setOrderLineItems(List.of(savedOrderLineItem));
order.setOrderTableId(savedOrderTable.getId());
order.setOrderStatus("COMPLETION");

// when
Order changedOrder = orderService.changeOrderStatus(orderId, order);

// then
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(changedOrder.getId()).isNotNull();
assertThat(changedOrder.getOrderStatus()).isEqualTo("COMPLETION");
});
}
}
Loading