post-custom-banner

이어서...

오늘은 작업을 많이 하지 못했다. 교회도 가고, 최근 너무 잠을 적게 자서(평균 4시간 수면) 잠을 몰아서 잤다. 한 12시간 안 되게 잔거 같은데, 이렇게 안 하면 정말 몸이 안 따라줄 것 같았다.

최근에는 허리 통증이 생기기 시작했다. 타이머를 설정해서 일정 시간마다 스트레칭하는 시간을 가져야겠다...

아무튼, 오늘 한 것은 딱 이거다.

1. TDD

입출력을 담당하는 InputView, OutputView와 랜덤 숫자 생성 테스트를 진행했다.


InputView


입력을 받는 코드는 1주차 코드를 그대로 사용했다. 테스트 코드에서 입력값을 직접 줄 수가 없으니 미리 InputStream 에 값을 넣어주고 테스트를 했다.

OutputView

(코드가 길어서 사진 대신 코드를 직접 넣었다.)

OutputView

package racingcar.tdd.view;

import java.util.List;

public class OutputView {
    private static final String carNameInputMessage = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)";
    private static final String attemptCountInputMessage = "시도할 회수는 몇회인가요?";
    private static final String gameResultMessage = "실행 결과";

    public static void printCarNameInputMessage() {
        System.out.println(carNameInputMessage);
    }

    public static void printAttemptCountInputMessage() {
        System.out.println(attemptCountInputMessage);
    }

    public static void printGameResultMessage() {
        System.out.println(gameResultMessage);
    }

    public static void printRoundResult(String carName, int moveLength) {
        String moveStatus = "-";
        System.out.printf("%s : %s\n", carName, moveStatus.repeat(moveLength));
    }

    public static void printWinner(List<String> winners) {
        String winnerMessage = String.join(", ", winners);
        System.out.println("최종 우승자 : " + winnerMessage);
    }
}

전진 결과를 출력하는 printRoundResult() 메서드는 인자로 받은 자동차 이름과 이동 정도를 문자열 형식을 사용해서 출력하게 한다. moveStatus.repeat() 을 사용하는 부분이 SRP 위반인지는 고민해봐야 한다.

OutputViewTest

package racingcar.tdd.view;

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

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;

public class OutputViewTest {
    private final ByteArrayOutputStream output = new ByteArrayOutputStream();
    
    private void setUpStreams() {
        System.setOut(new PrintStream(output));
    }

    private void doTest(String message) {
        assertThat(output.toString()).isEqualTo(message);
        System.setOut(System.out);
        output.reset();
    }


    @Test
    void 올바른_자동차_입력_메시지_출력시_통과() {
        String message = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)\n";

        setUpStreams();
        OutputView.printCarNameInputMessage();
        doTest(message);
    }
    @Test
    void 올바른_자동차_입력_요구_메시지_출력시_통과() {
        String message = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)\n";

        setUpStreams();
        OutputView.printCarNameInputMessage();
        doTest(message);
    }
    @Test
    void 올바른_시도_횟수_입력_메시지_출력시_통과() {
        String message = "시도할 회수는 몇회인가요?\n";

        setUpStreams();
        OutputView.printAttemptCountInputMessage();
        doTest(message);
    }

    @Test
    void 올바른_실행_결과_메시지_출력시_통과() {
        String message = "실행 결과\n";

        setUpStreams();
        OutputView.printGameResultMessage();
        doTest(message);
    }

    @Test
    void 올바른_이동_결과_메시지_출력시_통과() {
        String message = "Car1 : -\n";

        setUpStreams();
        OutputView.printRoundResult("Car1", 1);
        doTest(message);
    }

    @Test
    void 올바른_최종_우승자_메시지_출력시_통과() {
        List<String> winners = new ArrayList<>();
        winners.add("Car1");
        winners.add("Car2");

        String message = "최종 우승자 : Car1, Car2\n";

        setUpStreams();
        OutputView.printWinner(winners);
        doTest(message);
    }
}

InputViewTest 과는 정반대의 원리로 출력값을 가져온다.

PrintStream 을 만들고, System.out 대신 이 PrintStream 이 쓰이게 한다. 이후 출력하는 값들은 output 에 저장되고, 검증 후 다시 System.out 이 쓰이게 한다.

RandomNumberChecker

랜덤 숫자를 검증해서 boolean 값을 반환하는 메서드를 가진 클래스이다.

여기서 고민한게, >= 부분 때문에 판별식이 돼어서,
'랜덤 숫자 생성''랜덤 숫자 판별' 이라는 두개의 역할을 가지게 되어 SRP 위반이 아닌가 싶었다.

그래서 또 SRP 위반에 대해서 결론은.. SRP는 하나의 지침일 뿐이지 무조건적으로, 절대적으로 지켜야 하는 룰이 아니란 것이다.

만약 저 상황에서 '랜덤 숫자 생성''랜덤 숫자 판별' 이라는 역할을 분리한다면

return RandomnumberChecker.generateRandomNumber() >= passMin;

이런 코드를 가진 클래스가 만들어질 것이다. >= 하나 때문에, 너무 비효율적이지 않은가. 이 정도는 개발자의 자율적인 판단이 필요하다고 생각했다. 지킬건 지키되, 너무 경직되지 않고 유연한 자세가 필요하지 않을까?


마무리...

오늘은 많이 뭔갈 하지 못했다. 그래도 한 6시간은 과제에 사용했다. 여러가지 학습을 하고, 여기 적히지는 않았지만 커밋 컨벤션 관련 학습도 하고...

내일 목표는

1. 완료한 것들 커밋 컨벤션 지켜서 커밋하기
우테코에서 제공한 커밋 가이드 문서가 영어라서 해석하는데 시간을 좀 들여야 할 것 같다. 우테코 커뮤니티에도 좋은 커밋 메시지 만들기라는 게시글이 있는데, 이것도 참고해야겠다.

2. TDD 마무리하고 메인 코드로 옮기기

3. 과제 테스트 통과하기
사실상 여기까지는 얼마 안 걸릴것 같다.

4. 리팩토링
추가적인 기능들, 리뷰해주신 내용들, 사용하고자 했던 것들을 최대한 사용해볼 예정이다.

5. (시간이 된다면) 기능 목록이 정상 동작함을 테스트 코드로 확인하기
이번에 새로 추가된 요구사항인데, 관건은 다른 코드의 영향을 받지 않고 딱 그 기능만 테스트할 수 있느냐인 것 같다. 그래도 TDD로 많은 테스트를 진행해놨으니 엄청 오래 걸리지는 않을 것 같다.

새로 알게된 점

  1. SRP에 어느정도는 유연한 자세가 필요할 때가 있다. 특히 그것이 정말 사소한 곳에서 적용된다면!

좋았던 점

  1. 적은 시간이지만 프로그래밍을 할 수 있었던 것. 프로그래밍(또는 프로그래밍 공부)가 이젠 당연한 삶의 패턴이 된 것 같다. 오늘 교회에서 사람들과 얘기할 때도

    "이거(과제) 하는게 너무 재밌어서 이걸 하면서 프로그래머라는 직업에 대한 확신이 들었어요."

라고 말했다.

아쉬웠던 점

  1. 시간, 시간, 시간! 왜 사람은 잠을 자야만 하는 것인가.......
    내가 숏슬리퍼였다면 얼마나 좋았을까...

도움이 된 자료들

| System.in과 System.out에 대한 테스트 <- 2주차에 Input 테스트를 할 때 도움을 받았던 게시글이다.

| 객체지향 5원칙, SOLID

| 💠 완벽하게 이해하는 SRP (단일 책임 원칙)

profile
자바 백엔드 개발자
post-custom-banner

0개의 댓글