-4

I am trying to create a junit test on a method that calls for a user input using scanner and System.in.

The method being tested looks like this:

public void setUserAnswer(){
    Scanner input = new Scanner(System.in);
    userAnswer = input.nextInt();
}

Currently, I am using the following set up in jUnit:

StringBuilder sb = new StringBuilder();
        sb.append("");
        sb.append((problem.getOperand1()+problem.getOperand2()));
        String data = sb.toString();
        System.setIn(new ByteArrayInputStream(data.getBytes()));

operand1 and operand2 are random generated numbers which the user adds and enters the answer for.

The problem I have is the System.setIn doesn't pass from the jUnit class to the class being tested.

Help?

dunni
  • 43,386
  • 10
  • 104
  • 99
  • Post a complete example reproducing the problem. – JB Nizet Oct 04 '15 at 11:57
  • Where are you calling `System.setIn` ? Could you please post a full example? – Mureinik Oct 04 '15 at 11:58
  • I am not calling System.setIn.. i guess that is my issue. How do I pass the setIn into the original code? – Scholarmate Oct 04 '15 at 11:59
  • By complete example, I didn't mean the input. I meant the code. – JB Nizet Oct 04 '15 at 11:59
  • 1
    @Scholarmate why do you post code that calls System.setIn(), and then say you're not calling System.setIn(). This is more and more confusing. Post a complete code example: the class under test, and the JUnit test class. – JB Nizet Oct 04 '15 at 12:01
  • the code is too long to post here – Scholarmate Oct 04 '15 at 12:02
  • I am very new to java so sorry if I don't fully understand – Scholarmate Oct 04 '15 at 12:02
  • 1
    I'm not asking you to post your whole code. I'm asking you to post a complete example reproducing the problem. – JB Nizet Oct 04 '15 at 12:03
  • ...and why is this tagged eclipse? – eis Oct 04 '15 at 12:04
  • I tried posting the test method and it said it was too long. – Scholarmate Oct 04 '15 at 12:04
  • I am using eclipse to write my junit test and the testing class – Scholarmate Oct 04 '15 at 12:05
  • I just tested using [this code](http://pastebin.com/raw.php?i=7RFLQabn) and things are working just fine. You need to come up with some kind of test case that we can run which has a problem somewhere. Otherwise this should be closed. – eis Oct 04 '15 at 12:12
  • System.set in is used in the test class using jUnit. – Scholarmate Oct 04 '15 at 12:13
  • @eis yes that works fine with scanner and setin are in the same method... however, in my case they are in two different classes. – Scholarmate Oct 04 '15 at 12:16
  • Then create a test case showing the issue. I don't see why that would change anything. – eis Oct 04 '15 at 12:16
  • Using the code I posted I get the following result when System.out.println(data + " " + problem.getOperand1() + " " + problem.getOperand2() + " " + problem.getUserAnswer()); is printed: 41 20 21 0. The data being set using setin isn't being transfered to the class being tested – Scholarmate Oct 04 '15 at 12:17
  • [here](http://pastebin.com/raw.php?i=D3SGXLfP) is the same code using disctinct classes. No problems whatsoever. Create a similar test case like I have, which we can run, that shows the problem. – eis Oct 04 '15 at 12:19
  • Just to say it _very clear_: `System.setIn` sets the `System.in` stream for the _whole_ application. – Tom Oct 04 '15 at 12:23
  • I don't know what to say. I have one file called MathProblem.java that contains the method I want to test. I have another file called MathProblemTest that contains all of the jUnit methods. When I type the code I have, it doesn't pass the setin to the file being tested. – Scholarmate Oct 04 '15 at 12:23
  • Well, then I can tell you two things: 1. create a small and runnable example of your problem (and don't just try to post both complete classes, thanks). 2. make sure you call `setIn` _before_ the tested class creates the Scanner using `new Scanner(System.in)`. – Tom Oct 04 '15 at 12:29
  • [link](http://pastebin.com/d9Pq7R9h) is the jUnit Test and [link](http://pastebin.com/wzGxRLgu) is the code being tested. – Scholarmate Oct 04 '15 at 12:50
  • that's not very much code (which is a good thing). I wonder why the system would say that's too long, because it certainly isn't. – eis Oct 04 '15 at 13:50

2 Answers2

1

To cut the discussion, your method could be tested in following way:

public class MathProblem {

  private int userAnswer;

  public void setUserAnswer(){
    Scanner input = new Scanner(System.in);
    userAnswer = input.nextInt();
  }

  public int getUserAnswer() {
    return userAnswer;
  }
}

and now the Test:

public class MathProblemTest {

  private MathProblem mathProblem;

  @Before
  public void before() throws Exception {
    mathProblem = new MathProblem();
  }

  @Test
  public void testGetUserAnswer() throws Exception {
    StringBuilder sb = new StringBuilder();
    sb.append("41 20");
    String data = sb.toString();
    System.setIn(new ByteArrayInputStream(data.getBytes()));

    mathProblem.setUserAnswer();

    assertThat(mathProblem.getUserAnswer(), equalTo(41));
  }

}

As the others already mention: This code behaves as expected.

So if you do not see a correct behaviour the test might be broken or the problem is in another region of your code, that you have not posted. So ask your self: Where does your example differ from this example?

CoronA
  • 7,717
  • 2
  • 26
  • 53
0

You should not be messing with System.in during a unit test.

Ask yourself what it is you're testing. Are you testing the Scanner code, or the code that uses whatever values are entered by the user?

Your code probably does 3 things:

  • Ask user for input
  • Perform operation
  • Display result

In a unit test, you're testing the operation, not the user prompting or the display logic. Or if you do, they should be 3 different unit tests, and remember that if you're testing the user prompting, you're testing the use of the Scanner, not whether the Scanner can read System.in.

So, first split your code to:

public class MyClass {
    public static void main(String[] args) {
        Input input = promptUserForInput(new Scanner(System.in), System.out);
        Result result = performOperation(input);
        printResult(System.out, result);
    }
    // methods here
}

You don't have to create new classes for Input and Result if they are simple values. They could also be the same class, e.g. an instance of MyClass.

This way you can test if the operation works for various inputs, which is your primary concern.

@Test
public void testOperation1() {
    Input input = new Input(5, 15, true); // true means subtract
    Result result = MyClass.performOperation(input);
    assertEquals(-10, result.getValue());
}

@Test
public void testOperation2() {
    Input input = new Input(5, 15, false); // false means add
    Result result = MyClass.performOperation(input);
    assertEquals(20, result.getValue());
}

You can also test user prompting and result printing, if needed.

@Test
public void testPrompt() {
    String userInput = "5 15\nYes";
    PrintStream out = new PrintStream(new ByteArrayOutputStream());
    Input input = MyClass.promptUserForInput(new Scanner(userInput), out);
    assertEquals(5, input.getNum1());
    assertEquals(15, input.getNum2());
    assertTrue(input.isSubtractRequested());
}

@Test
public void testPrint() {
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    try (PrintStream out = new PrintStream(buf)) {
        MyClass.printResult(out, new Result(-10));
    }
    String outText = new String(buf.toByteArray());
    assertEquals("Result is -10\r\n", outText);
}
Andreas
  • 154,647
  • 11
  • 152
  • 247