
BooleanGenerator, Result, Input 의 구현체를 Controller를 만들 때 주입했다.컨트롤러는 단순히 뷰와 도메인을 이어주는 역할을 할 뿐, 어떤 뷰로 입력을 받거나 출력해야되는지 알 필요가 없다.
컨트롤러의 의존성을 모두 외부에서 주입하고 있네요 👍
한발 더 나아가서, 컨트롤러에 대한 테스트를 작성해볼 수도 있을까요? 만약 어렵다면 어떤 이유 때문일까요?
테스트가 필요하다. 그렇다면 어떻게?System.setIn(), System.setOut()등을 테스트 코드에서 활용하여 콘솔에 입력할 내용이나, 출력할 내용에 대해서 관리를 하고자 했다.System.setIn()과 같은 방식으로 콘솔에 입력할 값을 조작하여 테스트를 하는게 과연 Controller의
run()기능에 대한 테스트일까?
@DisplayName("자동차 경주 통합 정상 작동 테스트")
@Test
void playGameTest() {
String carNames = "헤나, 썬샷, 루카"+System.lineSeparator()+"5";
inputStream = new ByteArrayInputStream(carNames.getBytes(UTF_8));
out = new ByteArrayOutputStream();
outputStream = new PrintStream(out);
System.setIn(inputStream);
System.setOut(outputStream);
numberGenerator = new RandomNumberGenerator();
outputView = new OutputView();
inputView = new InputView();
racingCarController = new RacingCarController(inputView, outputView, numberGenerator);
racingCarController.newCarNames();
racingCarController.newGameRound();
racingCarController.play();
assertThat(out.toString()).contains("최종 우승했습니다.");
}
inputView, outputView가 잘 동작하는지에 대한 테스트도 포함된게 된다.public class MockInputView implements Input {
private final List<List<String>> inputPlayersNames;
private final List<Integer> inputHeightOfLadder;
private final List<List<String>> inputRewards;
private final List<List<String>> inputTargetPlayers;
private final List<String> inputContinue;
private int orderOfInputPlayerNames;
private int orderOfInputHeightOfLadder;
private int orderOfInputRewards;
private int orderOfInputTargetPlayers;
private int orderOfInputContinue;
public MockInputView(List<List<String>> inputPlayersNames,
List<Integer> inputHeightOfLadder,
List<List<String>> inputRewards,
List<List<String>> inputTargetPlayers,
List<String> inputContinue) {
this.inputPlayersNames = inputPlayersNames;
this.inputHeightOfLadder = inputHeightOfLadder;
this.inputRewards = inputRewards;
this.inputTargetPlayers = inputTargetPlayers;
this.inputContinue = inputContinue;
}
@Override
public List<String> inputPlayerNames() {
if (inputPlayersNames.size() == orderOfInputPlayerNames) {
orderOfInputPlayerNames = 0;
}
return inputPlayersNames.get(orderOfInputPlayerNames++);
}
@Override
public int inputHeightOfLadder() {
if (inputHeightOfLadder.size() == orderOfInputHeightOfLadder) {
orderOfInputHeightOfLadder = 0;
}
return inputHeightOfLadder.get(orderOfInputHeightOfLadder++);
}
// ...
}
orderOfInputXXX의 변수의 값을 하나씩 올려가며 체크하였다.inputHeightOfLadder()호출할 때 반환 값이 3, 5, 6, 3, 5, 6, 3, ... 순서대로 되도록 하였다.public class MockResultView implements Result {
private List<String> players;
private List<Line> ladder;
private List<Reward> rewards;
private Map<Player, Reward> gameResult;
private Boolean hasError;
@Override
public void printError(String errorMessage) {
this.hasError = true;
}
@Override
public void printLadder(Players players, Ladder ladder, Rewards rewards) {
this.players = players.getNames();
this.ladder = ladder.getLadder();
this.rewards = rewards.getRewards();
}
@Override
public void printGameResult(Map<Player, Reward> gameResult) {
this.gameResult = gameResult;
}
public Boolean hasError() {
return hasError;
}
public List<String> getPlayers() {
return players;
}
public List<Line> getLadder() {
return ladder;
}
public List<Reward> getRewards() {
return rewards;
}
public Map<Player, Reward> getGameResult() {
return gameResult;
}
}
public class MockBooleanGenerator implements BooleanGenerator {
private final List<Boolean> generatedBoolean;
private int orderOfBoolean;
public MockBooleanGenerator(List<Boolean> generatedBoolean) {
this.generatedBoolean = generatedBoolean;
}
@Override
public boolean generateBoolean() {
if (generatedBoolean.size() == orderOfBoolean) {
orderOfBoolean = 0;
}
return generatedBoolean.get(orderOfBoolean++);
}
}
@Test
@DisplayName("사다리 컨트롤러 정상 테스트")
void ladderControllerTest() {
//given
MockInputView inputView = new MockInputView(
List.of(List.of("a", "b", "c", "d")),
List.of(3),
List.of(List.of("1", "2", "3", "4")),
List.of(List.of("all")),
List.of("n"));
MockResultView resultView = new MockResultView();
MockBooleanGenerator booleanGenerator = new MockBooleanGenerator(List.of(true, false));
ladderController = new LadderController(inputView, resultView, booleanGenerator);
//when
ladderController.run();
List<String> resultPlayers = resultView.getPlayers();
List<Line> resultLadder = resultView.getLadder();
List<Reward> rewards = resultView.getRewards();
Map<Player, Reward> gameResult = resultView.getGameResult();
//then
assertThat(resultPlayers).isEqualTo(List.of("a", "b", "c", "d"));
assertThat(resultLadder.get(0).getLine()).isEqualTo(List.of(MOVABLE_BAR, UNMOVABLE_BAR, UNMOVABLE_BAR));
assertThat(resultLadder.get(1).getLine()).isEqualTo(List.of(MOVABLE_BAR, UNMOVABLE_BAR, UNMOVABLE_BAR));
assertThat(resultLadder.get(2).getLine()).isEqualTo(List.of(MOVABLE_BAR, UNMOVABLE_BAR, UNMOVABLE_BAR));
assertThat(rewards.get(0).getReward()).isEqualTo("1");
assertThat(rewards.get(1).getReward()).isEqualTo("2");
assertThat(rewards.get(2).getReward()).isEqualTo("3");
assertThat(rewards.get(3).getReward()).isEqualTo("4");
assertThat(gameResult.get(new Player(new Name("a"))).getReward()).isEqualTo("2");
assertThat(gameResult.get(new Player(new Name("b"))).getReward()).isEqualTo("1");
assertThat(gameResult.get(new Player(new Name("c"))).getReward()).isEqualTo("3");
assertThat(gameResult.get(new Player(new Name("d"))).getReward()).isEqualTo("4");
}
controller.run()은 많은 입력 값과 그에 대한 많은 실행 결과가 있어서 일 것 같다.어떠한 입력에서 의도한 대로 동작하여 관측 가능한 반환 값이나 상태를 변경시키는 지를 검증한다고 생각하여 짜도록 해보자.