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

[터틀] 지하철 경로 조회 - TDD 미션 제출합니다. #48

Merged
merged 30 commits into from
May 25, 2020
Merged

[터틀] 지하철 경로 조회 - TDD 미션 제출합니다. #48

merged 30 commits into from
May 25, 2020

Conversation

begaonnuri
Copy link
Member

감사합니다!

Copy link

@kang-hyungu kang-hyungu left a comment

Choose a reason for hiding this comment

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

안녕하세요 터틀 👋 리뷰어 구구입니다
코드 구현 깔끔하게 잘 하셨어요 👍
몇 가지 간단한 피드백 남겼으니 확인해보시고 궁금한 점 생기면 코멘트 남기거나 dm 주세요

public class SubwayDefaultDataConfiguration {
@Profile("local")
@Configuration
private class LocalDataApplicationRunner implements ApplicationRunner {

Choose a reason for hiding this comment

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

코드로 개발용 데이터 관리하는 것보다 data.sql로 데이터 초기화하는 게 어떨까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

프론트 화면을 테스트할 때 로직을 탄 검증된 데이터로 테스트하고싶어서 이렇게 했었습니다!
말씀해 주신 것 처럼 코드로 개발용 데이터를 관리하는 것이 적절하지 않은 것 같네요.

생각해 보니 장점이라면

  • 로직을 탄 검증된 데이터이고,

단점이라면

  • 실제 로직을 타기 때문에 약간의 성능 저하
  • 불필요한 코드 생성

이 있겠네요.
하지만 원하는 시나리오대로 data.sql을 만들었어도 가능했겠네요!
data.sql을 통해 초기화하도록 변경하겠습니다!
제 생각에 오류가 있다면 지적부탁드려요.

import wooteco.subway.admin.service.LineService;

import java.net.URI;
import java.util.List;

@RestController
@RequestMapping("/lines")
public class LineController {
private LineService lineService;

Choose a reason for hiding this comment

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

final을 붙여서 필수 멤버 변수라 표시해주는게 좋습니다

Copy link
Member Author

@begaonnuri begaonnuri May 18, 2020

Choose a reason for hiding this comment

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

final을 추가했습니다!
기능적인 차이는 크게 없지만 명시적으로 표현해주는것이라고 생각하는데
제 생각이 맞을까요?

Choose a reason for hiding this comment

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

차이가 있죠.
이 객체를 생성했을 때 필요한 멤버 변수를 초기화하도록 강제해서 객체를 설계한 의도대로 사용하도록 만들어줍니다

}

@GetMapping
public ResponseEntity<PathResponse> findPath(@ModelAttribute PathRequest pathRequest) {

Choose a reason for hiding this comment

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

RestController라 ModelAttribute 어노테이션 없어도 정상 동작하지 않나요?

Copy link
Member Author

@begaonnuri begaonnuri May 18, 2020

Choose a reason for hiding this comment

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

찾아보니 @RestController가 아닌 @Controller에서도 없어도 된다고 하네요!

image

저도 테스트해봤는데 되는것 같네요!
@ModelAttribute 를 제거하겠습니다 :)

@@ -15,12 +20,13 @@
private int intervalTime;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Set<LineStation> stations = new HashSet<>();
private Set<Edge> edges = new HashSet<>();

public Line() {

Choose a reason for hiding this comment

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

mock 객체 말고 쓰이는 곳이 없으니 private으로 만드는게 낫지 않을까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

private으로 변경하고, 기존 테스트 코드에서 사용되던 빈 생성자를 인자가 있는 생성자로 변경했습니다!

@@ -15,12 +20,13 @@
private int intervalTime;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Set<LineStation> stations = new HashSet<>();
private Set<Edge> edges = new HashSet<>();

Choose a reason for hiding this comment

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

만들어둔 Edges 클래스를 활용하는 것도 괜찮겠네요

Copy link
Member Author

Choose a reason for hiding this comment

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

Line에 Set를 사용하던 로직을 Edges로 이동했습니다!


import java.util.function.Function;

public class SubwayWeightEdge extends DefaultWeightedEdge {

Choose a reason for hiding this comment

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

상속 대신 DefaultWeightedEdge를 멤버 변수로 두는건 어떨까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

이번 미션에서 최단경로를 구하는 부분에서 외부 API를 사용했습니다.
DefaultWeightedEdge가 필요해서 처음엔 컴포지션을 고려했습니다.

image

위의 구현한 부분을 보시면 메소드의 접근제어가 protected라서 상속을 해야 한다고 생각했습니다!
혹시 다른 방법이 있다면 힌트를 주실 수 있을까요? 😉

Choose a reason for hiding this comment

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

라이브러리에서 강제하는 부분이라 상속을 쓸 수 밖에 없겠네요
따로 수정하지 않으셔도 됩니다!

@DisplayName("지하철 노선에서 지하철역 추가 / 제외")
@Test
void manageLineStation() {
StationResponse stationResponse1 = createStation(STATION_NAME_KANGNAM);

Choose a reason for hiding this comment

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

변수명을 강남역으로 쓰면 좀더 읽기 쉽지 않을까요?

Copy link
Member Author

@begaonnuri begaonnuri May 18, 2020

Choose a reason for hiding this comment

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

테스트코드라서 변수명을 홀대했네요🙄 값과 매칭되는 변수명으로 변경했습니다!

import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

public class PathAcceptanceTest extends AcceptanceTest {

Choose a reason for hiding this comment

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

새로운 api와 uri 경로가 추가될 때마다 AcceptanceTest가 너무 커지겠네요
get, post, put, delete에 대한 중복처리만 AcceptanceTest에서 제공하는게 어떨까요?

Copy link
Member Author

@begaonnuri begaonnuri May 18, 2020

Choose a reason for hiding this comment

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

최대한 중복되는 로직만 AcceptanceTest에서 관리하도록 하고,
나머지 로직은 해당 테스트 클래스로 이동했습니다!


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

class SubwayGraphTest {

Choose a reason for hiding this comment

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

예외 상황에 대한 테스트 케이스도 추가해보면 좋겠어요

Copy link
Member Author

Choose a reason for hiding this comment

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

예외 상황에 대한 테스트 케이스도 추가했습니다!

.collect(Collectors.collectingAndThen(Collectors.toList(), Lines::new));
}

public SubwayGraphs makeSubwayGraphs(Long sourceStationId, Long targetStationId) {

Choose a reason for hiding this comment

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

파라미터를 사용하지 않는데 의도한게 맞나요?

Copy link
Member Author

Choose a reason for hiding this comment

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

ㅎㅎ.. 인자 제거했습니다😅

@begaonnuri
Copy link
Member Author

리뷰 반영했습니다!
코멘트 별로 커밋 남겼습니다😉
또한 궁금한 점을 코멘트로 남겼는데, 답변해주시면 감사하겠습니다🙇🏻‍♂️

Copy link

@kang-hyungu kang-hyungu left a comment

Choose a reason for hiding this comment

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

터틀, 피드백 반영 잘 하셨어요
추가로 수정해보면 좋을 부분에 피드백 남겼어요
확인해보시고 궁금한 점 생기면 코멘트 남기거나 dm 주세요

lineRepository.save(persistLine);
@Transactional(readOnly = true)
public LineDetailResponse findLineWithStationsById(Long id) {
Line line = lineRepository.findById(id).orElseThrow(RuntimeException::new);

Choose a reason for hiding this comment

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

lineRepository.findById(id).orElseThrow(RuntimeException::new);

이 부분을 메서드로 추출하면 중복을 제거할 수 있겠네요

Copy link
Member Author

Choose a reason for hiding this comment

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

해당 부분을 메소드로 추출했습니다 :)

Long targetId = stations.findIdByName(target);

Lines lines = new Lines(lineRepository.findAll());
SubwayGraphs subwayGraphs = lines.makeSubwayGraphs(sourceId, targetId);

Choose a reason for hiding this comment

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

컴파일 에러가 발생하네요

Copy link
Member Author

Choose a reason for hiding this comment

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

꼼꼼하게 확인하지 못했네요 죄송합니다 😭

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.context.jdbc.Sql;
import wooteco.subway.admin.dto.*;
import wooteco.subway.admin.dto.LineDetailResponse;

Choose a reason for hiding this comment

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

api가 늘어나면 AcceptanceTest 클래스에 계속 메서드가 추가 되겠네요
아래 샘플 코드처럼 get, post, put, delete 호출에 대한 중복 코드를 제거하면 AcceptanceTest가 변경될 일은 없지 않을까요?

    <T> T post(String path, Map<String, String> params, Class<T> responseType) {
        return
                given().
                        body(params).
                        contentType(MediaType.APPLICATION_JSON_VALUE).
                        accept(MediaType.APPLICATION_JSON_VALUE).
                when().
                        post(path).
                then().
                        log().all().
                        statusCode(HttpStatus.CREATED.value()).
                        extract().as(responseType);
    }

Copy link
Member Author

Choose a reason for hiding this comment

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

제네릭을 이렇게 적용할수도 있군요! 테스트코드의 중복을 제거했습니다 :)

Long targetId = stations.findIdByName(target);

Lines lines = new Lines(lineRepository.findAll());
SubwayGraphs subwayGraphs = lines.makeSubwayGraphs(sourceId, targetId);

Choose a reason for hiding this comment

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

Lines 객체 생성할 때 sourceId, targetId, pathRequest.getKey() 까지 한번에 전달하면 이 로직도 Lines 객체에 감출 수 있지 않을까요?
PathDetail path = lines.getShortestPath();
이런식으로 실제 비즈니스 로직을 서비스 객체에 노출하지 않도록 변경할 수 있겠네요

Copy link
Member Author

Choose a reason for hiding this comment

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

해당 로직을 Lines로 이동했습니다!

비즈니스 로직을 서비스 객체에 노출하지 않도록

이 부분을 앞으로 주의해야겠네요!

Copy link

@kang-hyungu kang-hyungu left a comment

Choose a reason for hiding this comment

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

터틀, 피드백 반영한 내용 확인했어요
조금 더 보완해보면 좋을 부분에 코멘트 남겼어요
수정하시면서 궁금한 점 생기면 코멘트 남기거나 dm 주세요

jsonPath().getList(".", StationResponse.class);
}

StationResponse createStation(String name) {

Choose a reason for hiding this comment

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

AcceptanceTest는 모든 인수 테스트에서 공통적으로 사용할 기능만 남기는게 좋습니다.
getStations, createStation 같은 메서드는 StationAcceptanceTest로 옮겨주세요

Copy link
Member Author

Choose a reason for hiding this comment

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

getStations, createStation을 비롯해 특정 인수테스트에서만 사용하는 메소드를 분리했습니다!

Choose a reason for hiding this comment

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

아래 메서드들도 각 인수 테스트로 옮겨주세요
Station, Line 같이 특정 도메인과 관련된 테스트도 AcceptanceTest에 없는게 좋습니다
상속해서 사용하다가 수정사항이 생기면 다른 상속 받은 테스트에도 영향 받습니다

Copy link

@kang-hyungu kang-hyungu left a comment

Choose a reason for hiding this comment

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

머지 하기 전에 조금만 더 보완해보면 좋을 것 같아요
추가로 남긴 코멘트 한 번 더 확인해주세요!

jsonPath().getList(".", StationResponse.class);
}

StationResponse createStation(String name) {

Choose a reason for hiding this comment

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

아래 메서드들도 각 인수 테스트로 옮겨주세요
Station, Line 같이 특정 도메인과 관련된 테스트도 AcceptanceTest에 없는게 좋습니다
상속해서 사용하다가 수정사항이 생기면 다른 상속 받은 테스트에도 영향 받습니다

@begaonnuri
Copy link
Member Author

리뷰 반영했습니다 :)
코멘트로도, 디엠으로도 친절하게 답변해주셔서 감사합니다! 🙇🏻‍♂️

Copy link

@kang-hyungu kang-hyungu left a comment

Choose a reason for hiding this comment

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

터틀 🐢 피드백 반영 잘 하셨어요 👏
pr은 머지 처리할게요
코드 구현하느라 수고하셨어요!

addEdge(lineNumberTwo.getId(), hongik.getId(), shinchon.getId(), 10, 10);
addEdge(lineNumberTwo.getId(), shinchon.getId(), jamsil.getId(), 10, 10);
addEdge(lineNumberEight.getId(), jamsil.getId(), seokchon.getId(), 10, 10);
addEdge(lineNumberEight.getId(), seokchon.getId(), mongchon.getId(), 10, 10);

//when
WholeSubwayResponse wholeSubwayResponse = given().

Choose a reason for hiding this comment

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

다음 미션에서 이 부분도 수정해주세요!

@kang-hyungu kang-hyungu merged commit 5eef614 into woowacourse:begaonnuri May 25, 2020
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.

3 participants