Hi guys, I was trying to test a method which takes a user input, does something and then returns that input but I'm having some issues with it. I've attempted a few solutions, mostly described in SE, I will leaave them out of this
post and discuss instead the one that seemed the most likely to work instead.
This is the method that I'm trying to test (other code omitted for brevity):
public int getUserInput() {
int choice = 0;
boolean isValidInput = false;
System.out.printf("Welcome. Select action: %d for READ, %d for CREATE, %d for UPDATE, %d for DELETE, %d for EXIT.", OperationOptions.READ.getValue(), OperationOptions.CREATE.getValue(), OperationOptions.UPDATE.getValue(), OperationOptions.DELETE.getValue(), OperationOptions.EXIT.getValue());
while(!isValidInput)
try {
choice = scanner.nextInt();
if(choice == OperationOptions.READ.getValue() || choice == OperationOptions.CREATE.getValue() || choice == OperationOptions.UPDATE.getValue() || choice == OperationOptions.DELETE.getValue() || choice == OperationOptions.EXIT.getValue())
{
isValidInput = true;
}
else
{
isValidInput = false;
System.out.println(String.format(Messages.NOT_ALLOWED_VALUES, OperationOptions.READ.getValue(), OperationOptions.CREATE.getValue(), OperationOptions.UPDATE.getValue(), OperationOptions.DELETE.getValue(), OperationOptions.EXIT.getValue()));
}
scanner.hasNextLine();
}
catch(InputMismatchException e) {
System.out.println(Messages.NOT_NUMERICAL_ERROR);
isValidInput = false;
scanner.nextLine();
}
return choice;
}
A word about the test. What I had in mind was to test the actual input, so have a test that fakes the input, then calls the method and check that the fake input is the same as the one returned from that method. That would be
the first test, then chose another input and make sure it generates an exception and so on.
So this is my test class
package com.test.userInteraction;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class UserInputTest {
UserInput userInput;
private final InputStream systemIn = System.in;
private final PrintStream systemOut = System.out;
private ByteArrayInputStream testIn;
private ByteArrayOutputStream testOut;
@Before
public void setUpOutput() {
testOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(testOut));
}
private String getOutput() {
return testOut.toString();
}
@After
public void restoreSystemInputOutput() {
System.setIn(systemIn);
System.setOut(systemOut);
}
@Test
public void testCase1() {
int testString = 3;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out;
try {
out = new ObjectOutputStream(bos);
out.writeInt(testString);
out.close();
byte[] int_bytes = bos.toByteArray();
provideInput(int_bytes);
userInput = new UserInput();
int userInput2 = userInput.getUserInput();
assertEquals(testString, userInput2);
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void provideInput(byte[] int_bytes) {
testIn = new ByteArrayInputStream(int_bytes);
System.setIn(testIn);
}
}
So the problem is that it fails when it reachesint userInput2 = userInput.getUserInput();
inside getUserInput
when it reaches choice = scanner.nextInt();
it generates a InputMismatchException and then controls goes back to choice = scanner.nextInt();
and then fails but I can't quite figure out why
The stack trace in the test is
java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:862)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at com.test.userInteraction.UserInput.getUserInput(UserInput.java:34)
at com.test.userInteraction.UserInputTest.testCase1(UserInputTest.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Any idea what is going on? It's like it doesn't like the fake input somehow