오늘은 점심 약속이 있어, 많은 공부와 개발을 진행하지는 못했다. 하지만 보람차고, 즐거운 하루였다. 점심 약속 전까지 약 3시간 동안 아주 알차게 독서 와 Product 도메인 개발을 진행했다.
Product 개발
오늘은 개발할 시간이 많지 않아, 어제 설계한 도메인에 대한 테스트를 작성하고, 테스트를 어떻게 세분화할지 고민하였다. 지난 Member 개발 간 받은 피드백 중 하나였던 테스트의 부족에 관하여 생각이 많다. 테스트 본질의 목적인, 로직의 검증 뿐만아니라 어떻게하면 테스트로 코드 문서처럼 작성할 수 있을까? 하는 고민으로 가득하다.
좋은 테스트에 대하여
좋은 테스트는 어떤 테스트일까? 이에대한 정해진 답은 없다. 하지만 오답은 있다. 그럼 오답만 피한다면, 내가 생각한 것이 정답이 될 수도 있지 않을까?
내가 생각하는 좋은 테스트가 아닌 조건은 테스트가 테스트의 역할을 제대로 하지 못하는 것이다. 테스트의 역할은 요구사항을 검증하여 프로젝트에 대한 확신을 갖게 해주는 것이다. 그런데 테스트가 테스트의 역할을 못한다면? 그것이 바로 좋은 테스트가 아니다라고 분명하게 말할 수 있지 않을까?
이제 좋은 테스트가 되기 위한 조건을 생각해보았을 때, 좋은 테스트는 명확해야한다. 무엇을 테스트하는지 명확해야한다. 뿐만아니라 어떤 흐름으로 이뤄지는지, 테스트의 흐름 또한 명확해야한다. 명확성이라는 기준이 좋은 테스트의 기준이 될 수있을 것 같다.
분명 이외에도 좋은 테스트가 되기 위한 조건(좋은 테스트가 갖춘 특성)은 다양할 것이다. 좋은 테스트에 관한 여러 사람들의 관점과 생각이 궁금하다.
나는 명확한 테스트를 작성하기 위한 방법을 생각하면서 어떻게하면 테스트가 좀 더 쉽게 읽힐까? 하는 고민을 하게되었다.
테스트가 쉽게 읽힌다는 것은 테스트가 하나의 글처럼 읽히는 것에서부터 시작이지 않을까? 그래서 나는 테스트는 글(문서)이다. 에 초점을 맞추었다.
테스트가 문서처럼 읽히기 위해서 어떤식으로 테스트를 작성해야할까? 나는 Given-When-Then 패턴을 사용하여 테스트가 좀 더 쉽게 읽하고자 하였다.
나는 테스트 관련된 공부를 진행하면서 given-when-then 패턴만으로 충분히 표현력 있는 테스트를 작성할 수 있다 생각하여, 테스트 작성은 given-when-then 만을 사용해왔다. 그러다 GPT에게 피드백을 요청했다.
@Test
@DisplayName("Test validation of invalid price")
public void validateInvalidPrice() {
// Arrange
Money invalidMoney = new Money(new BigDecimal("-200.00"));
Product invalidPriceProduct = Product.builder()
.productId(new ProductId(UUID.randomUUID()))
.productCategory(ProductCategory.SHOES)
.description("Test Product Description")
.price(invalidMoney)
.productImages(List.of(new ProductImage(new ProductImageId(UUID.randomUUID()), "TestUrl")))
.build();
invalidPriceProduct.initializeProduct();
// Act & Assert
assertThatThrownBy(invalidPriceProduct::validateProduct)
.isInstanceOf(ProductDomainException.class)
.hasMessageContaining("Price must be greater than zero!");
}
같은 테스트인데 주석(패턴)이 바뀌었다. 의미적으로 본다면 크게 다른것이 없어보인다.
given에 매칭되는 Arrage 는 테스트를 진행하기 위한 준비과정이며, Act 는 When 으로 동작할 때, 작동할 때 를 의미하므로 이 또한 같은 의미이다. Assert의 경우 약간 다르다고 생각되는 부분인데 then에서는 그래서 이럴거야! 한다면, assert는 명확하게 비교를 한다.
비슷한듯 다른 두 테스트 방식에서 어느것이 더 문서같은 테스트가 될 수 있을까? 두 패턴모두 테스트를 여러 단계로 분리하여 가독성을 중요시했다는 것은 분명하다. 이에 대해 나는 사람마다 다르게 생각하겠지만, GWT 패턴이 좀더 글처럼 읽힐 수 있지 않을까? 생각을 해본다.(하지만 당연히 무엇보다 개발팀의 사용하는 패턴을 따라야할것이다.)
그리고 추가적으로, GPT를 통해 깨알같이 모르는 부분에 대해서 배울 수 있어서 정말 좋은 것같다.
Member 추가 피드백
지난 마지막 Member 피드백에 대한 응답 후, 추가 이야기가 없어 Member PR을 마무리지었다. 그런데 그저께 코멘트가 달렸다는 메일을 확인하면서 알게되었다.
내가 피드백에 대한 응답으로 작성한 코멘트는 언급이 없었다. 그래서 리뷰어가 해당 응답을 보지 못한것 같다.
먼저 의문이 되는 부분이 handler 의 책임과 helper 의 책임에 대해 물었다.
handler 는 웹 계층에서의 요청에 대한 처리를 담당한다. 그래서 DTO(Data Transfer Object) 인 것이다. 그리고 이 DTO를 통해 도메인 계층에 접근할 수 있다. 따라서 Handler의 책임을 이야기한다면, 웹 계층에서의 요청(입력)을 도메인계층으로 전달하는 것 이라고 이야기할 수 있다.
Helper의 역할로는 ApplicationService 내 유스케이스의 중복되는 처리로직을 재사용하기위해 모듈화 한것이다. 그리고 이를 통해 CommandHandler 를 단순화 하는 것이다.
각 유스케이스를 사용하기 위해 각 CommandHandler를 통해 도메인 로직에 접근한다. 그리고 이 과정에서 MemberHelper 가 사용된다.
다른 리뷰어들이 보기에 중복되는 메서드가 하나뿐이 없는데, 다른 모듈로 빼서 관리하는게 더 나은것인가? 하는 생각이 들수있을 것 같다. 이에 대한 나의 생각은 조금 다르다.
각 CommandHandler의 코드들 중 중복되는 내용은 saveMember() 만 중복되는 것이 아니다. MemberDomainService, MemberRepository 가 공유된다.(두 클래스 모두 각 CommandHandler에 의존성이 주입된다.) 나는 두 클래스가 중복된다는 것은 중복되는 코드가 점점 더 많아 질 수 있음을 의미한다고 생각한다.
지금이야 단 3개..뿐인 유스케이스와 초 심플한 비즈니스 로직으로 인한 너무 간단한 상황이지만, 이게 고도화 된다면? 프로젝트의 목표인 유지보수성과, 가독성(표현성)을 고려한다면?
나는 하나의 클래스(MemberHelper)를 생성하여 중복을 줄이고, 관리 포인트를 하나 더 만드는 것을 선택했다.
그런데 이번 대화를 통해 하나 내가 놓친부분을 깨달았다.
나는 Spring을 처음 사용해본다.. 기본적인 내용은 책과 검색을 통해 공부하고 바로 지금 신발주문시스템을 개발하기 시작했다. 스프링을 공부하면서 빈을 관리한다는 것을 알고 있었는데, 친구와의 대화를 통해 내가 제대로 알고 있지 않음을 알 수 있었다.
지금도 빈이 등록되고 사용되는 것에 대해 제대로 모른다.. 같은 빈에 의존하기 위해 컨테이너 레지스트리가 있는것 아니여? 하는 말을 보면 뇌정지가 오는 것같다. 빈을 사용하고 응용하는 방법에 대해 공부를 해봐야겠다..
'회고 > TIL' 카테고리의 다른 글
Product 도메인 계층 개발, 입력 유효성 검사, 테스트 (0) | 2023.06.29 |
---|---|
클린아키텍처, 유스케이스 구현하기 (0) | 2023.06.28 |
Product 도메인 설계 및 개발 시작, 만들면서 배우는 클린아키텍처 (0) | 2023.06.26 |
6월 마지막 주 주말, 클린아키텍처 공부 (0) | 2023.06.25 |
PR 작성, 피드백, 개선 (0) | 2023.06.22 |