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단계] 수달(허수진) 미션 제출합니다 #205

Merged
merged 12 commits into from
Oct 24, 2022

Conversation

her0807
Copy link

@her0807 her0807 commented Oct 23, 2022

안녕하세요 더즈 수달 입니다.😀
미션을 하면서 마치 처음 회사에 가서 도메인 파악을 하는 그런 느낌을 받았습니다. ㅎㅎㅎ

Javadoc 을 활용해 도메인 이해도 높이기

요구사항 도출하는데 시간이 조금 걸렸어요. 더즈도 이해하기 어려우실 것 같아서 Javadoc 활용해서 Test 마다 작업 내역에 대한
문서화 해두었습니다.

ServiceTest 객체는 무엇인가?

  1. 현재 각 도메인마다 다른 Mock 객체를 사용해서 중복되는 객체도 있고, Application Context 가 여러번 뜨고 있었어요.
    그 문제를 해결하기 위해서 부모 객체를 만들어 상속구조로 중복 제거와 Context 를 재사용하도록 변경해두었습니다.

  1. 또한 Service 비지니스 로직만을 검증하기 위해서는 협력관계에 있는 DAO 를 격리해야했기 때문에 Mocking 처리 해두었는데요.
    내부 로직이 테스트 코드에 들어난다는 점에서 마음에 들지 않더라고요. 그래서 한번 추상화를 해서 상위 객체에 관련 로직을 둠으로 캡슐화를 시도해보았어요.

  1. 추가로 테스트와 픽스처가 한글로 작성이 되어 있는데, 테스트는 문서화의 기능도 있다고 생각하여 전달력이 좋은 방향을 선택해보았습니다.

CustomIllegalArgumentException

현재 검증 로직의 대부분이 IllegalArgumentException 예외를 반환하고 있는데, 테스트에서 구체적으로 어떤 내용에 대한 예외인지
명시해주고 싶어서 메세지를 담게 되었어요. 표준 예외에 메세지를 담으려고 하니까 ExceptionType.getMessage() 와 같이 데이터를
꺼내야하는 일이 중복해서 발생하고 있었어요. 따라서 ExceptionType 이라는 예외관리하는 객체와 협력하는 객체를 하나 만들어서 위 작업을 진행하면 재밌겠다는 생각에 만들어보았어요.

추후 계획

Test 에 대한 내용이 머지되면 빠르게 서비스 계층에 있는 레거시들을 각자 책임으로 돌려놓는 작업을 할 예정입니다.

  • Transactional 클래스 최상단으로 올리기
  • 서비스에 있는 도메인 검증 로직 도메인으로 내리기 .. 등등..

그래서 2단계 들어가면 테스트 구조나 내용이 바뀔 수 있을 것 같아요. ㅎㅎㅎ 이점 참고해서 리뷰 주시면 감사하겠습니다.

Copy link

@ldk980130 ldk980130 left a comment

Choose a reason for hiding this comment

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

안녕하세요 수달! 더즈입니다. 1단계 요구사항인 요구사항 작성과 테스트를 잘 작성해 주셨어요. 리드미도 테스트도 읽기 좋아서 리뷰하는데 오래 걸리지 않았어요.

전체적으로 고칠 것도 없고 생각해볼만한 점만 남겨놓고 머지하겠습니다. 2단계에서 리팩터링이 어떻게 이루어질지 기대가 됩니다. 1단계 수고하셨어요!

Comment on lines +85 to +88
> ## 수달이 이해한 도메인 ✍🏻
키친 포스는 오프라인 주문을 받을 수 있는 서비스에요.
오프라인 기준으로는 매장에서 주문을 직접 받고 주문에 대한 상태관리를 해요.
상태에는 주문이 후 조리가 시작되고, 손님이 식사를하고, 계산하는 순서가 있겠죠?

Choose a reason for hiding this comment

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

리드미 작성이 너무 친절하네요.. 이런 꼼꼼한 점은 저도 배워가고 싶어요! 💯💯

Comment on lines +90 to +91
매장에서는 특수하게 한 사람이 여러 테이블을 계산할 수도 있어요. 더즈가 팀장이 되면
금요일 주말에 스모디 사람들에게 한턱 쏠때 처럼요. ^^ 그 때 관리를 하기 위한 테이블 그룹이라는 도메인도 있다는 점이 신기하네요.

Choose a reason for hiding this comment

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

제가 한턱 내는 건가요...?ㅋㅋㅋㅋ

Comment on lines +3 to +13
public enum ExceptionType {

NOT_FOUND_MENU_EXCEPTION("없는 메뉴가 포함된 주문입니다."),
NOT_FOUND_TABLE_EXCEPTION("없는 테이블에 대한 요청입니다."),
INVALID_CHANGE_ORDER_STATUS_EXCEPTION("이미 완료된 주문입니다."),
INVALID_PROCEEDING_TABLE_GROUP_EXCEPTION("진행중인 테이블 그룹이 존재합니다."),
INVALID_PRODUCT_PRICE_EXCEPTION("유효하지 않은 상품 가격입니다."),
INVALID_TABLE_GROUP_EXCEPTION("그룹 구성은 두개 이상의 테이블 부터 가능합니다."),
INVALID_TABLE_UNGROUP_EXCEPTION("아직 그룹을 해지할 수 없습니다."),
INVALID_CHANGE_NUMBER_OF_GUEST("게스트 숫자는 음수일 수 없습니다.");

Choose a reason for hiding this comment

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

수달네 프로젝트에서도 이렇게 예외 관리를 하셨나요? 저희 팀에서도 하나의 비즈니스 예외 클래스에 내부 상태로 여러 상황들을 넣는 식으로 관리했어요.

이렇게 하니까 커스텀 예외 갯수를 작게 유지하면서 여러 상황을 표현해서 클라이언트에게 전달할 수 있다는 장점이 있는 것 같아요 👍

Comment on lines +32 to +49
@SpringBootTest
public class ServiceTest {

@MockBean
protected MenuDao menuDao;
@MockBean
protected MenuGroupDao menuGroupDao;
@MockBean
protected MenuProductDao menuProductDao;
@MockBean
protected ProductDao productDao;
@MockBean
protected OrderDao orderDao;
@MockBean
protected OrderLineItemDao orderLineItemDao;
@MockBean
protected OrderTableDao orderTableDao;

Copy link

@ldk980130 ldk980130 Oct 24, 2022

Choose a reason for hiding this comment

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

현재 각 도메인마다 다른 Mock 객체를 사용해서 중복되는 객체도 있고, Application Context 가 여러번 뜨고 있었어요. 그 문제를 해결하기 위해서 부모 객체를 만들어 상속구조로 중복 제거와 Context 를 재사용하도록 변경해두었습니다.

서비스 레이어를 테스트할 때 통합 테스트를 진행할 수도 있고 수달처럼 DAO 계층을 모의해서 슬라이스 테스트를 할 수도 있다고 생각합니다. 이건 테스트하려는 개발자의 의도와 관련있다고 생각해서 정답이 있다고 생각되지는 않아요.

그리고 애플리케이션 컨텍스트를 재사용하기 위해서 부모 클래스로 통합 처리한 방법은 좋은 방법이라고 생각해요 저도 즐겨쓰는 방법입니다.

그런데 이왕 목 객체를 사용한다고 했으면 과연 @SpringBootTest가 필요한지 의문이기는 해요. 사실 mock만 제대로 주입해 준다면 컨텍스트를 띄울 필요가 없기도 하고요.

@ExtendWith(MockitoExtension.class)
public class ServiceTest {

    @Mock
    protected MenuDao menuDao;
    @Mock
    protected MenuGroupDao menuGroupDao;
    @Mock
    protected MenuProductDao menuProductDao;
    @Mock
    protected ProductDao productDao;
    @Mock
    protected OrderDao orderDao;
    @Mock
    protected OrderLineItemDao orderLineItemDao;
    @Mock
    protected OrderTableDao orderTableDao;

저라면 컨텍스트를 띄우지 않고 위와 같이 Dao의 목 객체들을 세팅해 놓고 @SpringBootTest 없이 슬라이스 테스트를 진행해서 테스트 속도를 개선해볼 수도 있을 것 같아요. 수달은 어떻게 생각하나요?

ServiceTest를 상속받은 테스트에서 아래처럼 mock을 주입하면 테스트는 돌아갈 겁니다.
image

스프링을 사용하고 있지만 과연 @SpringBootTest가 꼭 필요한지, 없이 할 수 있다면 없이 하는 것도 방법이라 생각해요!

Comment on lines +53 to +75
protected void 완료된_주문_조회() {
Mockito.when(orderDao.findById(anyLong())).thenReturn(Optional.of(주문_생성(OrderStatus.COMPLETION)));
}

protected void 메뉴존재유뮤세팅(Long count) {
Mockito.when(menuDao.countByIdIn(any())).thenReturn(count);
}

protected void 존재하지않는_테이블_세팅() {
Mockito.when(orderTableDao.findById(anyLong())).thenReturn(Optional.empty());
}

protected OrderTable 테이블_생성(Long id) {
return new OrderTable(id, 1L, 1, false);
}

protected void 테이블_그룹이_없는_테이블_세팅(Long id) {
Mockito.when(orderTableDao.findById(anyLong())).thenReturn(Optional.of(new OrderTable(id, null, 1, false)));
}

protected void 존재하는_테이블_세팅() {
Mockito.when(orderTableDao.findById(anyLong())).thenReturn(Optional.of(테이블_생성(1L)));
}

Choose a reason for hiding this comment

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

서비스 레이어의 내부 로직이 드러나지 않도록 한 번 감춘 코드라고 하셨는데 가독성 면에서도 크게 좋아진 것 같아요! 👍

@ldk980130 ldk980130 merged commit 6279fbb into woowacourse:her0807 Oct 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants