JUnit test for void method (output-input dependency) - java

I came up with a problem while trying to write a JUnit test for one specific method.
I searched for possible solutions but many of them were not helpful as the output was not dependent on the input. Any help would be much appreciated.
My class method looks like this:
public static void method1() {
Scanner input = new Scanner(System.in);
String state = input.nextLine();
if( /* condition dependent on state value */ ) {
System.out.println("...");
} else {
System.out.println("..."+state+"...");
}
}
How to write a JUnit test for it, can Robot class somehow solve the problem?

If you extract the logic to a separate function like
public static void method1() {
Scanner input = new Scanner(System.in);
String state = input.nextLine();
System.out.println(processingLogic(state));
}
static String processingLogic(String state) {
if ( /* condition dependent on state value */) {
return "some value";
} else {
return "some other value";
}
}
you can write a unit test for that function to see that it works correctly.

You can manipulate System.in and System.out using System.setIn() etc. For instance
System.setIn(new ByteArrayInputStream("test data".getBytes()));
However, I have the feeling that for your case, changing your method in a way that #p-j-meisch suggested in https://stackoverflow.com/a/59513668/2621917 is much better.

Related

How to deal with an unitialized constructor in an if statement (Java)

I've run into a problem where I attempt to define a constructor in the first part of a switch/case statement, and then I can't run the code because the program can't get the definition later.
The idea behind passing the constructor information from a switch/case function is that the user chooses what to do, but for some options, one must be done before the other is possible (e.g. Create password and Check password).
If I try doing it this way, it throws a VarMayNotHaveBeenInitialized error (I get the sense the answer is in a try/catch statement, but I don't know enough about them to be sure). I've included some code that is what I've been essentially trying to do below. (The two classes are to best simulate the project I'm working on.)
Any help is appreciated! : )
TestMain.java:
package exitTest;
public class TestMain {
InitializeTest init;
public static void main(String[] args) {
while (true) {
String x = InitializeTest.askQuestion();
switch (x) {
case "set":
InitializeTest init = new InitializeTest();
break;
case "get":
if (init != null) {
init.showExample();
} else {
System.out.println("Error: init not initialized.");
} break;
}
}
}
}
InitializeTest.java:
package exitTest;
import java.util.Scanner;
public class InitializeTest {
static Scanner in = new Scanner(System.in);
public InitializeTest thing1;
public String example;
public static String askQuestion() {
System.out.println("set for set example\nget for check example");
String action = in.nextLine();
return action;
}
public InitializeTest() {
System.out.println("Input string:");
String example = in.nextLine();
}
void showExample() { System.out.println(example); }
}
You include the type when you're declaring variables, not when simply assigning to an existing one. When you write
InitializeTest init = new InitializeTest();
That makes a new init variable, unrelated to the previous one, which stores the newly constructed object. That new variable shadows the existing one, but it gets released after the switch block is over (variables in Java are block-scoped).
To put it to an analogy, it's as though you wanted to tell your friend Alice a secret. But when you went to her house, her neighbor whose name is also Alice happened to be there instead. If you tell that Alice your secret, then your friend doesn't find out. Even though the two happen to share a name, they don't share any memory.

JUnit Emulating Console Input [duplicate]

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.

How to fix a word requesting program?

I created a JAVA code, and I don't have any errors, but when I run the code, the output does this:
Enter a word: Thank you for entering a word! And it does not let me enter anything, when I intend for the code to let me enter a word, then it checks if it is a word, and gives the answer if it is a word, or none if it isn't. (It is my first time asking on this site) Here's the code:
package files;
import java.util.Scanner;
public class Testprinter {
static boolean myBoolean = false;
static Scanner userInput = new Scanner(System.in);
public static void main(String[] args){
String usersInput;
while(myBoolean != true)
{
System.out.print("Enter a word: ");
usersInput = userInput.toString();
myBoolean = checkInput(usersInput);
}
checkifComplete();
}
public static boolean checkInput(String usersInput){
if(usersInput == (String)usersInput)
{
return true;
} else { return false; }
}
public static void checkifComplete(){
if(myBoolean = true){
System.out.print("Thank you for entering a word!");
}
}
}
This line is wrong:
if (usersInput == (String)usersInput)
It should be:
if (usersInput.equals(usersInput))
In Java, strings (and in general: all objects, that is all types that are non-primitive) must me compared using the equals() method, which tests for equality. The == operator is fine for testing equality between primitive types, but for objects it tests for identity - a different concept, and 99% of the time, not what you want.
And besides, you're comparing a string with itself! it'll always return true, I'm quite sure that's not what you want to do… notice that the parameter must have a different name, currently it's called just like the attribute. Perhaps this is what you meant?
public static boolean checkInput(String input) {
return usersInput.equals(input);
}
You forgot scanner.nextLine(); thats reason its not asking you enter anything.
Instead of usersInput = userInput.toString();
Use:
String usersInputStr = scanner.nextLine();
Follow this link - for how to use scanner: How can I read input from the console using the Scanner class in Java?
Your issue is using userinput.toString(), when you should be using usersInput = userInput.next();. You are currently retrieving the string representation of the scanner, not getting a word.
Corrected main:
public static void main(String[] args){
String usersInput;
while(myBoolean != true)
{
System.out.print("Enter a word: ");
usersInput = userInput.next();
myBoolean = checkInput(usersInput);
}
checkifComplete();
}

JUnit test for console input and output

I have only one method main. How to check System.out.println() and replace Scanner to input values automatically using JUnit?
P.S. Please, provide some solutions...
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] arr = new int[4];
for (int i = 0; i < arr.length; i++) {
arr[i] = scanner.nextInt();
}
for (int i = 0; i < arr.length; i++) {
int res = 0;
int k = 0;
int num = arr[i];
/*.....*/
System.out.println(num);
}
}
Ideally, extract the awkward dependencies so that you can test without them. Change main to simply:
public static void main(String[] args) {
doWork(new Scanner(System.in), System.out);
}
// TODO: Rename to something meaningful
public static void doWork(Scanner input, PrintStream output) {
// Remainder of code
}
(Consider using a Writer instead of a PrintStream for output.)
Then you don't really need to unit test main - but you can test doWork using a Scanner based on a StringReader, and output based on a StringWriter, providing whatever input you want and checking the output.
I faced a similar issue and this is what I ended up doing.
First off, I'd suggest doing as #Jon-Skeet suggests and instead of using the main(String[]) method of the class, create a separate method.
Then you can have that method take in an InputStream as a parameter and then create a Scanner object within the method that uses the passed InputStream as its source. That way you can pass any InputStream, such as System.in, to the method when it's called (elaboration below).
package my.package;
import ...;
public class MyClass
{
public static void myMethod(InputStream inputStream)
{
Scanner inputScanner = new Scanner(inputStream);
// Do stuff with the Scanner such as...
String input = inputScanner.nextLine();
System.out.println("You inputted " + input);
}
}
Now, in your production source code you can call myMethod and pass it System.in as an argument as such, myMethod(System.in);
And then in your unit tests, you can create mock input values via a ByteArrayInputStream:
package my.package;
import ...;
public class MyClassTest
{
#Test
void testMyMethod()
{
// Simulates a user inputting the string "Mock input" and hitting enter
assertDoesNotThrow(myMethod(new ByteArrayInputStream("Mock input\n".getBytes())));
}
}
And voila, you now have a way to pass your method mock input as well as it being more modular overall.
I just want to point out without getting too much into it, that when working with System.in, one needs to be careful about closing it and in unit tests when working with input streams, one needs to be careful about reusing a reference to the same InputStream as its state can persist across uses.

Try...catch vs if...else for exceptions - Java

What if I want to take user input from the args[0] array, but just in case I (the user) forgot to define it, I wanted a prompt to come up - is it better to use an if block to determine whether the array item(s) is empty or not, or to catch the exception? So, is this
public class Stuff {
static Scanner input = new Scanner(System.in);
public static void main(String[] args) {
String foo;
if(args.length > 0) {
foo = args[0];
}
else {
foo = getString("Input? ");
}
}
public static String getString(String prompt) {
System.out.print(prompt + " ");
String answer = input.nextLine();
return answer;
}
}
better or worse than
public class Stuff {
static Scanner input = new Scanner(System.in);
public static void main(String[] args) {
String foo;
try {
foo = args[0];
}
catch(ArrayIndexOutOfBoundsException e) {
foo = getString("Input? ");
}
}
public static String getString(String prompt) {
System.out.print(prompt + " ");
String answer = input.nextLine();
return answer;
}
}
Your first example will still throw an exception since in the if statement you are still accessing an index which does not exist.
If you want to use an if statement then you should be checking that the length of the array is greater than the index you are trying to access for example:
if(args.length > 0)
foo = args[0];
You need to test args.length rather than reading args[0]
But apart from that error, it is better to use if/else for the following reasons:
It makes the code clearer
It has better performance (although not relevant in this case)
There is no exceptional condition (it is expected that the args array may be empty in some circumstances e.g. "user error", so the code should be able to handle it). Exception throwing should be reserved for exceptional situations that shouldn't happen.
IMO, if-else is better/faster in this case.
Throwing an exception is used when you are inside a method and you want to say to the caller that something went wrong, and you can't do it with the return value.
But as said Jon Taylor, your if statement won't work that way.
All you really need is a single line of code.
final String foo = args.length > 0? args[0] : getString("Input? ");
Use if block to check whether array is empty that is easy and faster.
Given the correction evidenced by Jon Taylor, I will prefer the version with if.
Not only for the speed gain (that in your example I guess will be not noticeable), but because the code with the if better explains its intents, simplifying future maintenance on the code.

Categories