반응형
2021-06-11글
중복되는 테스트 메서드
- 중복되는 테스트 메서드는 추출하자
- 테스트코드도 유지보수의 대상이며 하나의 문서이기 때문에 가독성을 고려하자
private ExtractableResponse<Response> addSection(String content) {
return RestAssured.given().log().all()
.body(content)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.when()
.post("/lines/{id}/sections", 1L)
.then().log().all()
.extract();
}
예외 처리는 Service에서
- DAO에서 예외를 체크해주고 있었음
- DAO는 말그대로 DB에 어세스하는 역할만 할 뿐
- 이 결과에 대한 예외는 서비스가 알아서 하도록 하자
@Valid와 @Validated로 DTO 검증
@Valid
를 통해 DTO에서 요청에 대한 검증을 하게 해줌- Validation을 group화 할 수 있는 점을 이용해서 각기 요청마다 group을 지어 한 DTO를 사용해도 각각 요청에 맞는 검증을 진행
정리
Valid로 잡은 예외 메시지 처리
- Valid 어노테이션으로 잡은 MethodArgumentNotValidException의 메시지를 제대로 출력하고 있지 않았음
- 게이츠가 제안해준 방법을 적용하니 BindingResult에 있는 예외 메시지들을 추출하여 던져줄 수 있었음
Service 레이어에서 도메인이 아닌 DTO를 반환하자
- Controller에서 도메인을 가져다 써서 화면에 필요한 데이터를 표현하기 위해 도메인 자체나 도메인의 getter를 노출시키고 있었음
- 도메인의 정보를 외부(view)에 노출하는 경우, interface인 DTO를 통해 서비스 레이어에서 반환하도록 리팩토링
- 참고
정리
RestAssured에 대해
- 전 단계까지는 MockMVC 등으로 컨트롤러단을 단위테스트로 진행했음
- E2E 테스트를 한다는 것은 말 그대로 끝부터 끝까지,요청부터 내가 원하는 응답을 테스트 하는 것인데
이렇게되면 단위 테스트를 하는 이유가 있을까라는 고민을 했음 - 고민한 결과 "단위 테스트는 구현 단계에서 내가 구현한 레이어(단위)가 정상 동작하는지를 테스트하기 위해,
이후 모든 단위들이 조합되었을 때 통합테스트를 통해 애플리케이셔이 원하는 기능을 잘 수행하는지 테스트한다"
이렇게 결론을 내림
구간 추가 로직
- 구간을 추가할 때 상행역, 하행역이 존재하는지 또 이를 찾고 수정해주기 위한 많은 로직을 작성해야했음
- 그런데 사실 비슷한 로직인데 상행인지 하행인지 대상만 달랐음
- 아래와 같이 일단 구현을 목적으로 했을 때는 엄청난 분기가 생겨버림
@Transactional
public void addSection(final Long lineId, final SectionRequest sectionRequest) {
Line line = lineRepository.findById(lineId);
Section toAddSection = sectionRequest.toSection(lineId);
Station targetStation = line.registeredStation(toAddSection);
if (toAddSection.hasUpStation(targetStation)) {
Section targetSection = line.findSectionWithUpStation(targetStation);
checkAddableByDistance(toAddSection, targetSection);
lineRepository.updateSection(lineId,
new Section(targetSection.id(), lineId, toAddSection.downStation(), targetSection.downStation(), targetSection.subtractDistance(toAddSection)));
}
if (toAddSection.hasDownStation(targetStation)) {
Section targetSection = line.findSectionWithDownStation(targetStation);
checkAddableByDistance(toAddSection, targetSection);
lineRepository.updateSection(lineId,
new Section(targetSection.id(), lineId, targetSection.upStation(), toAddSection.upStation(), targetSection.subtractDistance(toAddSection)));
}
lineRepository.addSection(lineId, sectionRequest.getUpStationId(), sectionRequest.getDownStationId(), sectionRequest.getDistance());
}
- 이는 상행구간 찾기, 하행구간 찾기를 전략패턴을 이용해 리팩토 해버렸음
public class LineService {
private final LineRepository lineRepository;
private final StationDao stationDao;
private final List<FindSectionStrategy> findSectionStrategies;
// ...
- 일단 LineService가 구간을 찾는 전략을 가지고 있음
@Transactional
public void addSection(final Long lineId, final SectionRequest sectionRequest) {
Line line = lineRepository.findById(lineId);
Section toAddSection = sectionRequest.toSection(lineId);
Station targetStation = line.registeredStation(toAddSection);
Section targetSection = line.findSectionWithStation(targetStation, findSectionStrategies);
lineRepository.updateSection(lineId, targetSection.updateToAdd(toAddSection));
lineRepository.addSection(lineId, sectionRequest.getUpStationId(), sectionRequest.getDownStationId(), sectionRequest.getDistance());
}
- 대상 구간을 찾을 때 전략들을 주입하고
public Section findSectionWithStation(Station targetStation, List<FindSectionStrategy> findSectionStrategies) {
return findSectionStrategies.stream()
.map(findSectionStrategy -> findSectionStrategy.findSection(sections, targetStation))
.filter(Optional::isPresent)
.map(Optional::get)
.findAny()
.orElse(EMPTY);
}
- Sections에서 다음과 같이 해당 구간을 찾음
PR 링크
- STEP1
- [STEP2](
반응형
'우아한테크코스 > 미션 정리' 카테고리의 다른 글
Level2. atdd-subway-fare 정리 (0) | 2021.08.06 |
---|---|
Level2. atdd-subway-path 정리 (0) | 2021.08.06 |
Level2. jwp-chess 정리 (0) | 2021.08.06 |
[코드리뷰 정리] Level 1. 체스 미션 - Service Layer, DAO와 Repository (0) | 2021.08.06 |
[코드리뷰 정리] Level 1. 체스 미션 - JDBC (0) | 2021.08.06 |