JUnit testing with simulated user input - java

I am trying to create some JUnit tests for a method that requires user input. The method under test looks somewhat like the following method:
public static int testUserInput() {
Scanner keyboard = new Scanner(System.in);
System.out.println("Give a number between 1 and 10");
int input = keyboard.nextInt();
while (input < 1 || input > 10) {
System.out.println("Wrong number, try again.");
input = keyboard.nextInt();
}
return input;
}
Is there a possible way to automatically pass the program an int instead of me or someone else doing this manually in the JUnit test method? Like simulating the user input?

You can replace System.in with you own stream by calling System.setIn(InputStream in).
InputStream can be a byte array:
InputStream sysInBackup = System.in; // backup System.in to restore it later
ByteArrayInputStream in = new ByteArrayInputStream("My string".getBytes());
System.setIn(in);
// do your thing
// optionally, reset System.in to its original
System.setIn(sysInBackup);
Different approach can be make this method more testable by passing IN and OUT as parameters:
public static int testUserInput(InputStream in,PrintStream out) {
Scanner keyboard = new Scanner(in);
out.println("Give a number between 1 and 10");
int input = keyboard.nextInt();
while (input < 1 || input > 10) {
out.println("Wrong number, try again.");
input = keyboard.nextInt();
}
return input;
}

To test drive your code, you should create a wrapper for system input/output functions. You can do this using dependency injection, giving us a class that can ask for new integers:
public static class IntegerAsker {
private final Scanner scanner;
private final PrintStream out;
public IntegerAsker(InputStream in, PrintStream out) {
scanner = new Scanner(in);
this.out = out;
}
public int ask(String message) {
out.println(message);
return scanner.nextInt();
}
}
Then you can create tests for your function, using a mock framework (I use Mockito):
#Test
public void getsIntegerWhenWithinBoundsOfOneToTen() throws Exception {
IntegerAsker asker = mock(IntegerAsker.class);
when(asker.ask(anyString())).thenReturn(3);
assertEquals(getBoundIntegerFromUser(asker), 3);
}
#Test
public void asksForNewIntegerWhenOutsideBoundsOfOneToTen() throws Exception {
IntegerAsker asker = mock(IntegerAsker.class);
when(asker.ask("Give a number between 1 and 10")).thenReturn(99);
when(asker.ask("Wrong number, try again.")).thenReturn(3);
getBoundIntegerFromUser(asker);
verify(asker).ask("Wrong number, try again.");
}
Then write your function that passes the tests. The function is much cleaner since you can remove the asking/getting integer duplication and the actual system calls are encapsulated.
public static void main(String[] args) {
getBoundIntegerFromUser(new IntegerAsker(System.in, System.out));
}
public static int getBoundIntegerFromUser(IntegerAsker asker) {
int input = asker.ask("Give a number between 1 and 10");
while (input < 1 || input > 10)
input = asker.ask("Wrong number, try again.");
return input;
}
This may seem like overkill for your small example, but if you are building a larger application developing like this can payoff rather quickly.

One common way to test similar code would be to extract a method that takes in a Scanner and a PrintWriter, similar to this StackOverflow answer, and test that:
public void processUserInput() {
processUserInput(new Scanner(System.in), System.out);
}
/** For testing. Package-private if possible. */
public void processUserInput(Scanner scanner, PrintWriter output) {
output.println("Give a number between 1 and 10");
int input = scanner.nextInt();
while (input < 1 || input > 10) {
output.println("Wrong number, try again.");
input = scanner.nextInt();
}
return input;
}
Do note that you won't be able to read your output until the end, and you'll have to specify all of your input up front:
#Test
public void shouldProcessUserInput() {
StringWriter output = new StringWriter();
String input = "11\n" // "Wrong number, try again."
+ "10\n";
assertEquals(10, systemUnderTest.processUserInput(
new Scanner(input), new PrintWriter(output)));
assertThat(output.toString(), contains("Wrong number, try again.")););
}
Of course, rather than creating an overload method, you could also keep the "scanner" and "output" as mutable fields in your system under test. I tend to like keeping classes as stateless as possible, but that's not a very big concession if it matters to you or your coworkers/instructor.
You might also choose to put your test code in the same Java package as the code under test (even if it's in a different source folder), which allows you to relax the visibility of the two parameter overload to be package-private.

I managed to find a simpler way. However, you have to use external library System.rules by #Stefan Birkner
I just took the example provided there, I think it couldn't have gotten more simpler:
import java.util.Scanner;
public class Summarize {
public static int sumOfNumbersFromSystemIn() {
Scanner scanner = new Scanner(System.in);
int firstSummand = scanner.nextInt();
int secondSummand = scanner.nextInt();
return firstSummand + secondSummand;
}
}
Test
import static org.junit.Assert.*;
import static org.junit.contrib.java.lang.system.TextFromStandardInputStream.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.TextFromStandardInputStream;
public class SummarizeTest {
#Rule
public final TextFromStandardInputStream systemInMock
= emptyStandardInputStream();
#Test
public void summarizesTwoNumbers() {
systemInMock.provideLines("1", "2");
assertEquals(3, Summarize.sumOfNumbersFromSystemIn());
}
}
The problem however in my case my second input has spaces and this makes the whole input stream null!

You might start by extracting out the logic that retrieves the number from the keyboard into its own method. Then you can test the validation logic without worrying about the keyboard. In order to test the keyboard.nextInt() call you may want to consider using a mock object.

I have fixed the problem about read from stdin to simulate a console...
My problems was I'd like try write in JUnit test the console to create a certain object...
The problem is like all you say : How Can I write in the Stdin from JUnit test?
Then at college I learn about redirections like you say System.setIn(InputStream) change the stdin filedescriptor and you can write in then...
But there is one more proble to fix... the JUnit test block waiting read from your new InputStream, so you need create a thread to read from the InputStream and from JUnit test Thread write in the new Stdin... First you have to write in the Stdin because if you write later of create the Thread to read from stdin you likely will have race Conditions... you can write in the InputStream before to read or you can read from InputStream before write...
This is my code, my english skill is bad I hope all you can understand the problem and the solution to simulate write in stdin from JUnit test.
private void readFromConsole(String data) throws InterruptedException {
System.setIn(new ByteArrayInputStream(data.getBytes()));
Thread rC = new Thread() {
#Override
public void run() {
study = new Study();
study.read(System.in);
}
};
rC.start();
rC.join();
}

I've found it helpful to create an interface that defines methods similar to java.io.Console and then use that for reading or writing to the System.out. The real implementation will delegate to System.console() while your JUnit version can be a mock object with canned input and expected responses.
For example, you'd construct a MockConsole that contained the canned input from the user. The mock implementation would pop an input string off the list each time readLine was called. It would also gather all of the output written to a list of responses. At the end of the test, if all went well, then all of your input would have been read and you can assert on the output.

Related

How to have the console support input for System.in in Idea unit tests

When unit testing in IntelliJ IDEA Education Edition, the console does not support System.in input.
The code I wrote is as follows:
#Test
void main() {
Scanner scanner = new Scanner(System.in);
int judge = 1;
while (judge == 1)
{
int n = scanner.nextInt();
System.out.println("输入的数为:" + n);
if (n == 0)
judge = 0;
}
}
When I run the test, I cannot enter it from the console and show that the test passed. I knew that unit tests didn't need input from the console to complete the test, but I happened to find out about the problem and wondered how to fix it. Just start learning unit tests, teach us a lot!
When doing unittest you have two components: The unittest itself and the code under test (cut).
Your example looks like as if you try to build a unittest by simply adding the #Test annotation to the tested code. This does not work.
As a start you should create a class containing you production code (the cut) and another class with the same name appended with Test:
class ProductionCode {
// something to test
}
and
class ProductionCodeTest {
// the tests for the production code
}
The responsibility of the *Test class is to set up a test environment for the cut. In your case this involves to replace any dependencies (as System.in or the Scanner object wrapping it and System.out) with test doubles. The best way to do this it to use a mocking framework like Mockito:
#ExtendWith(MockitoExtension.class)
class ProductionCodeTest {
#Mock
private Scanner input;
#Mock
private PrintWriter output;
private ProductionCode cut;
#BeforeEach
void setup(){
cut = new ProductionCode(input,output);
}
}
But this raises the problem that you need a possibility to replace the real dependency in your cut. This is done preferably via *constructor injection:
class ProductionCode {
private final Scanner scanner;
private final PrintWriter output;
ProductionCode(Scanner scanner, PrintWriter output){
this.scanner = scanner;
this.output = output;
}
void main(){
int judge = 1;
while (judge == 1)
{
int n = scanner.nextInt();
output.println("输入的数为:" + n);
if (n == 0)
judge = 0;
}
}
}
Now you can verify the expected behavior of your cut which is the return value (which you don't have) and/or communication with its dependencies, which is the methods called and parameters passed:
#ExtendWith(MockitoExtension.class)
class ProductionCodeTest {
#Mock
private Scanner input;
#Mock
private PrintWriter output;
private ProductionCode cut;
#BeforeEach
void setup(){
cut = new ProductionCode(input,output);
}
#Test(name="returns when user entered '0'")
void test(){
// arrange / given
Mockito.when(input.nextInt()).thenReturn(1,2,3,4,5,0);
// act / when
cut.main();
// assert / then
Mockito.verify(output, Mockito.times(6))
.println(Mockito.anyString());
}
}
Conclusion
Unittesting is more that just writing a random annotation to some code written. Your example became complex very quickly because you choose a rather hard to test scenario to start. I'd suggest a pure function like a method that adds two numbers and returns the result as a better starting point and deal with the dependency stuff later.

How to test a function that returns a string, but has no inputs in Java

So I'm new in java and in testing and I keep trying to test this function but i could not find a way. Any test goes. The function is part of bigger class called SignUp, that has other methods aswell. I know its not very well done :)
public static String newDayOfBirth() {
Scanner scanner = new Scanner(System.in);
String dayOfBirth = scanner.nextLine();
if(Integer.parseInt(dayOfBirth) > 0 && Integer.parseInt(dayOfBirth) < 32){
return dayOfBirth;
}
else {
System.out.println("Eneter a valid day of birth");
dayOfBirth = scanner.nextLine();
return dayOfBirth;
}
}
The problem is that there is a scanner involved. You really need to split out the handling of input from System.in to somewhere else and change this method to take that text as input and parse and validate that input.
Restrict methods to one piece of behaviour in order to unit test them and isolate behaviour that can't be unit tested (eg handling manual input from system.in) and cover that in integration tests.
You'd want to invoke it in the test and then check validity of response ( what is a valid date of birth? Any valid date? One with age more than X?)
You can mock Scanner using PowerMocktio using
#PrepareForTest({
TargetClass.class,
Scanner.class
})
#RunWith(PowerMockRunner.class)
public class Test {
private TargetClass target;
#Before
public void setUp() {
target = new TargetClass();
}
#Test
public void shouldDoSomething() {
// Arrange
whenNew(Scanner.class).withAnyArgument().thenReturn(mockedScanner);
when(scanner.nextLine()).thenReturn("line");
// Act
String actual = target.newDayOfBirth();
// Assert
assertEquals("line", actual);
}
}

Java Passing variables, avoiding toString hash

I know this has been asked before, but not in a way I understood, because I am dumb.
So.
I need to take some variables into a class, compare them against something, and then return the higher of the two. In the long run, I need to compare against a running total, but for my problem, I think the issue is considerably more fundamental. I'm not understanding how to pass a variable BACK to my main class.
import java.io.*;
public class testing123 {
public static void main(String[] args) {
InputStreamReader input = new InputStreamReader(System.in);
BufferedReader reader = new BufferedReader(input);
Integer a;
Integer b;
Integer numbersCombined;
try {
System.out.println("Please enter a number");
a = Integer.parseInt(reader.readLine());
System.out.println("Please enter a number");
b = Integer.parseInt(reader.readLine());
numbersCombined = (a + b);
testClass Check = new testClass();
System.out.println("Your numbers combined is " +numbersCombined);
System.out.println(Check);
} catch (IOException e){
System.out.println("Error reading from user");
}
}
}
class testClass {
public static Integer testClass (Integer numbersCombined) {
if (numbersCombined > 100) {
numbersCombined = numbersCombined;
}
else {
numbersCombined = 100;
}
System.out.println(numbersCombined);
return numbersCombined;
}
}
If I remove the return, this will print the numbersCombined, but that's all it does. With the return in place, it doesn't execute the print line above the return, and first prints the original numbersCombined (which it shouldn't if you use, say, 10 and 20, since that's less than 100), and then prints testClass#76046e53 rather than the actual value. I know there's a way to override it, but the answers I've found don't work for me.
I know this answer: http://www.google.com/url?q=http%3A%2F%2Fstackoverflow.com%2Fquestions%2F29140402%2Fhow-do-i-print-my-java-object-without-getting-sometype2f92e0f4&sa=D&sntz=1&usg=AFQjCNGIzxlBSH8xIS7hurKe6_Euc7B8RQ
is the basic problem I'm encountering, but the overrides listed aren't really working for me, and I want integer anyway, rather than string.
In the end, what I'm "really" doing is taking a series of 4 numbers from a user, then using this function to compare whether THIS series of numbers is higher than the previous maximum, and if it is, that's the new maximum moving forward, with a loop until the user is done entering serieses of 4 numbers, and then finally printing the maximum.
I was able to write this without ANY functions, all inline, easy as pie. But once I send the comparison to a function, I don't understand how to send it back, and I've spent all day trying to understand the concept. ALL DAY. So, while I know it's going to be a stupid answer, that's because I'm stupid, but not because I didn't try (sorry, kind of defensive. Frustrated).
Fundamentally, I want to send two (this example is just one) variables to a class, compare them, change ONE of them, and return it to the main class. In this example, I'm just trying to send ONE variable, compare it, and the send it back.
You need to call the method within TestClass. Your code is already returning an integer from that method.
Once you instantiate the class run testClass.testClass(numbers)
The way you're throwing around pseudo-global variables between classes is probably the problem. Pass them through the calls like above, rather than implicitly.
Try to do something like this:
import java.io.*;
public class HelloWorld{
public static void main(String []args){
InputStreamReader input = new InputStreamReader(System.in);
BufferedReader reader = new BufferedReader(input);
Integer a;
Integer b;
Integer numbersCombined;
try {
System.out.println("Please enter a number");
a = Integer.parseInt(reader.readLine());
System.out.println("Please enter a number");
b = Integer.parseInt(reader.readLine());
numbersCombined = (a + b);
testClass Check = new testClass(numbersCombined); // constructor should be like this
System.out.println("Your numbers combined is " + numbersCombined);
System.out.println(Check);
} catch (IOException e){
System.out.println("Error reading from user");
}
}
}
class testClass {
Integer numbersCombined;
// This is a constructor
public testClass (Integer numbersCombined) {
if (numbersCombined > 100) {
this.numbersCombined = numbersCombined; // use this to represent the object
} else {
this.numbersCombined = 100;
}
System.out.println(numbersCombined);
}
// Add method toString()
public String toString() {
return this.numbersCombined.toString();
}
}

How do I reuse my scanner?

I made a utility class with a method that is takes user input, and returns a lower case version of it so I don't have so much repetitive code, but it could only be used once. After that, it wouldn't scan. What can I do to fix this?
p.s. I have seen a lot of people saying it is a bad idea to reuse a scanner. Why is this? Shouldn't it be fine if the scanner is reset?
public String scan() {
String input;
Scanner s = new Scanner(System.in);
if (s.hasNextLine()) {
input = s.nextLine();
} else {
input = "ERROR";
}
s.reset();
s.close();
return input.toLowerCase();
}
You are correct every time you use the scanner its getting reseted. A solution to your problem would be the following:
Add all the parameters that you want to pass to scan() in an array and using a for loop pass them all to scan() while when returning, adding them again in their respective array position so then you can easily reuse them wherever you want.
You could actually reuse your Scanner instead of creating a new one each time you need to read a line , here is an example:
public class Test {
public static void main(String[] args) {
Test t = new Test();
//create your scanner object
Scanner s = new Scanner(System.in);
//send it as a parameter whenever you need to read a line
System.out.println(t.scan(s));
System.out.println(t.scan(s));
System.out.println(t.scan(s));
//close it after finishing
s.close();
}
public String scan(Scanner s) {
String input;
if (s.hasNextLine()) {
input = s.nextLine();
} else {
input = "ERROR";
}
return input.toLowerCase();
}
}
But if you make a reader utility class for you it's more logical to be an Instance variable in the utility class .
For Example:
public class Reader {
Scanner s ;
public Reader() {
s = new Scanner(System.in);
}
public String scan() {
String input;
if (s.hasNextLine()) {
input = s.nextLine();
} else {
input = "ERROR";
}
return input.toLowerCase();
}
public void close(){
s.close();
}
}
And when you use it , it will be like this :
public class Main {
public static void main(String[] args) {
//create your scanner object
Reader r = new Reader();
//send it whenever you need to read a line
System.out.println(r.scan());
System.out.println(r.scan());
System.out.println(r.scan());
//close it after finishing
r.close();
}
}
I have seen a lot of people saying it is a bad idea to reuse a
scanner. Why is this?
It is not a bad idea to reuse a scanner object in most cases. Without further context, it is hard to determine their reasons for saying that. It could've been they were mistaken. Maybe you reused it incorrectly when they stated that. Who knows?
One thing is certain: When you invoke Scanner#close() in an object that is reading from System.in, you are also closing the underlying input stream. And, once the input stream is closed, you cannot reopen it in the life of the application.
Shouldn't it be fine if the scanner is reset?
Scanner#reset() doesn't do what you think it does. In fact, in this code example it does nothing. This method resets the Locale to US, and the radix back to base 10.
Code Analysis
public String scan() {
String input;
Scanner s = new Scanner(System.in);
if (s.hasNextLine()) {
input = s.nextLine();
} else {
input = "ERROR";
}
s.reset();
s.close(); // BAD IDEA!!!!
return input.toLowerCase();
}
This method will only work one time for the reasons I already stated: "once the input stream is closed, you cannot reopen it in the life of the application." So, what you do instead? If you require to reuse this Scanner object over and over, it might be better to make it a global attribute of the class and you should never close it. This is obviously a bad idea. So, you might be better off using some other type of input stream wrapper.
Maybe this is why other people have told you in the past that reusing the scanner object was a bad idea. It is only speculation. But, judging from this code example, it seems like a very strong possibility.

how to create multiple scanner elements in java [duplicate]

This question already has an answer here:
How to use multiple Scanner objects on System.in?
(1 answer)
Closed 26 days ago.
I have a main function in which I use scanner to read an integer from console.
Inside this main function, we can access another function which also uses scanner to read an integer. So, the program swings between these two functions many times. But, Java.util.scanner throws an exception. Is there any way to overcome this?
import java.util.Scanner;
public class dummy {
public static void main(String[] args) {
int buy;
Scanner sc = new Scanner(System.in);
buy = sc.nextInt();
user = dummy2();
sc.close();
}
static boolean dummy2(){
Scanner sc1 = new Scanner(System.in);
sc1.close();
}
}
First of all, it would make the question much easier to answer if you gave more information, such as the exception and its message, and maybe source code.
If the exception is a NoSuchElementException, the direct problem is that the function is closing the Scanner. When the scanner is closed, it also closes the underlying ImputStream. This makes all other Scanner on that input invalid.
If the exception is InputMismatchException, then the input is not an int.
If the exception is IllegalStateException, then the scanner has been closed, this could happen is the function and the main method are using the scanner, and one closes it.
However, you should not be taking user input in functions. This limits future use, say if you wanted to later add a GUI or make the same calculation based off a number not gotten from the user, then you would need rewrite the function. The function should take a int as a parameter, which the main method should get from the user. Only the main method and other methods directly relating to user input, such as the Scanner's methods, should read user input.
Use the same Scanner object.
import java.util.Scanner;
public class dummy {
private static final Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
int buy;
buy = sc.nextInt();
user = dummy2();
//Do more stuff with the same scanner
//close it when done
}
static boolean dummy2(){
//Scan stuff
int nbr = sc.nextInt();
}
I would suggest something like that:
import java.util.Scanner;
public class dummy {
Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
int buy;
buy = sc.nextInt();
user = dummy2();
sc.close();
}
static boolean dummy2(){
//lets scan a string.
sc.nextLine();
}
}
Reusable objects! Isn't that nice?

Categories

Resources