Skip to content

Commit

Permalink
Refactoring Main to be more testable
Browse files Browse the repository at this point in the history
Added tests for AgeGuesserUI as well.
  • Loading branch information
dackerman committed May 3, 2016
1 parent df6a3ee commit 810bdd3
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 26 deletions.
32 changes: 32 additions & 0 deletions src/main/java/com/dacklabs/ageguesser/AgeGuesserUI.java
@@ -0,0 +1,32 @@
package com.dacklabs.ageguesser;

public class AgeGuesserUI {
private final ConsoleIO io;
private final AgeGuesser guesser;

public AgeGuesserUI(ConsoleIO io, AgeGuesser guesser) {
this.io = io;
this.guesser = guesser;
}

public void start() {
io.printLine("I will guess your age, and you tell me if it was higher, lower, or yes.");
boolean correct = false;
while (!correct) {
int guess = guesser.guess();
io.printLine(String.format("Are you %d years old? (possible answers: higher, lower, yes)", guess));
String answer = io.getUserInput().toLowerCase();

if (answer.contains("higher")) {
guesser.addHint(HintDirection.HIGHER);
} else if (answer.contains("lower")) {
guesser.addHint(HintDirection.LOWER);
} else if (answer.contains("yes")) {
correct = true;
} else {
io.printLine(String.format("I didn't understand \"%s\". Try again", answer));
}
}
io.printLine(String.format("Hooray! I only needed %d hints!", guesser.hintsReceived()));
}
}
7 changes: 7 additions & 0 deletions src/main/java/com/dacklabs/ageguesser/ConsoleIO.java
@@ -0,0 +1,7 @@
package com.dacklabs.ageguesser;

public interface ConsoleIO {
String getUserInput();

void printLine(String line);
}
28 changes: 2 additions & 26 deletions src/main/java/com/dacklabs/ageguesser/Main.java
@@ -1,32 +1,8 @@
package com.dacklabs.ageguesser;

import java.util.Scanner;

import static com.dacklabs.ageguesser.HintDirection.HIGHER;
import static com.dacklabs.ageguesser.HintDirection.LOWER;

public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.println("I will guess your age, and you tell me if it was higher, lower, or correct.");
AgeGuesser guesser = new AgeGuesser();
boolean correct = false;
while (!correct) {
int guess = guesser.guess();
System.out.println(String.format("Are you %d years old? (possible answers: higher, lower, correct)", guess));
String answer = scanner.nextLine().toLowerCase();

if (answer.contains("higher")) {
guesser.addHint(HIGHER);
} else if (answer.contains("lower")) {
guesser.addHint(LOWER);
} else if (answer.contains("correct")) {
correct = true;
} else {
System.out.println(String.format("I didn't understand \"%s\". Try again", answer));
}
}
System.out.println(String.format("Hooray! I only needed %d hints!", guesser.hintsReceived()));
AgeGuesserUI ui = new AgeGuesserUI(new SysIO(), new AgeGuesser());
ui.start();
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/dacklabs/ageguesser/SysIO.java
@@ -0,0 +1,18 @@
package com.dacklabs.ageguesser;

import java.util.Scanner;

public final class SysIO implements ConsoleIO {

private final Scanner scanner = new Scanner(System.in);

@Override
public String getUserInput() {
return scanner.nextLine();
}

@Override
public void printLine(String line) {
System.out.println(line);
}
}
66 changes: 66 additions & 0 deletions src/test/java/com/dacklabs/ageguesser/AgeGuesserUITest.java
@@ -0,0 +1,66 @@
package com.dacklabs.ageguesser;

import org.junit.Test;

import static org.junit.Assert.*;

public class AgeGuesserUITest {

@Test
public void uiGuessesAgeAndCelebratesIfCorrect() {
FakeIO io = new FakeIO("yes");
AgeGuesser guesser = new AgeGuesser();

AgeGuesserUI ui = new AgeGuesserUI(io, guesser);
ui.start();

assertEquals(3, io.output.size());

String expectedFirstLine = "I will guess your age";
String expectedSecondLine = String.format("Are you %d years old? ", guesser.guess());
String expectedThirdLine = "Hooray! I only needed 0 hints";

assertTrue("first message wasn't instructions",
io.output.get(0).contains(expectedFirstLine));

assertTrue("second message doesn't contain the guess",
io.output.get(1).contains(expectedSecondLine));

assertTrue("third message doesn't contain a count of guesses",
io.output.get(2).contains(expectedThirdLine));
}

@Test
public void uiKeepsGuessingAgeUntilCorrect() {
FakeIO io = new FakeIO("higher", "lower", "yes");

AgeGuesserUI ui = new AgeGuesserUI(io, new AgeGuesser());
ui.start();

assertEquals(5, io.output.size());

String ageQuestionRegex = "Are you \\d+ years old.*";

assertTrue(io.output.get(1).matches(ageQuestionRegex));
assertTrue(io.output.get(2).matches(ageQuestionRegex));
assertTrue(io.output.get(3).matches(ageQuestionRegex));
}

@Test
public void uiTellsUserWhenItGetsAnInvalidCommandAndContinues() {
FakeIO io = new FakeIO("aljsfl;kasjdf", "yes");

AgeGuesserUI ui = new AgeGuesserUI(io, new AgeGuesser());
ui.start();

assertEquals(5, io.output.size());

String secondLine = io.output.get(2);
String expectedSecondLine = "I didn't understand \"aljsfl;kasjdf\"";
String lastLine = io.output.get(4);

assertTrue("Expected invalid command message but got " + secondLine, secondLine.contains(expectedSecondLine));
assertTrue("Expected Hooray but got " + lastLine, lastLine.contains("Hooray!"));
}

}
30 changes: 30 additions & 0 deletions src/test/java/com/dacklabs/ageguesser/FakeIO.java
@@ -0,0 +1,30 @@
package com.dacklabs.ageguesser;

import java.util.ArrayList;
import java.util.List;

public class FakeIO implements ConsoleIO {

private int lineIndex = 0;
private final String[] lines;

// This is public so the test can inspect it easily
public final List<String> output = new ArrayList<>();

// lines are what will pretend to be coming from System.in
public FakeIO(String... lines) {
this.lines = lines;
}

@Override
public String getUserInput() {
// return the next line that was passed in
return lines[lineIndex++];
}

@Override
public void printLine(String line) {
// the SUT calls this, and we just record it for later inspection
output.add(line);
}
}

0 comments on commit 810bdd3

Please sign in to comment.