Open In App

Unit Testing System.in for Input Handling in JUnit

Last Updated : 25 Nov, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

In Java applications, it’s common to read input from System.in for interactive console applications. Unit testing such methods can be challenging because they rely on user input. We can achieve this by redirecting System.in to a ByteArrayInputStream that contains the input we want to simulate.

In this article, we will learn how to perform unit testing of System.in in Java using JUnit.

Maven Dependencies

Include the below JUnit 5 dependencies into the pom.xml file to set up the testing environment.

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>

Unit Testing of System.in

The purpose of unit testing is to ensure that individual pieces of code, functions or methods, in isolation are correct. Testing becomes more difficult when a method relies on external input such as the user input through System.in. By default, System.in expects users to provide input through the console, but in automated tests, we want to simulate this input programmatically.

The basic idea is to convert System.in to another input stream, such as ByteArrayInputStream, which allows automatic, repeatable hands-free testing. Basic steps include:

  1. Redirecting System.in: Use ByteArrayInputStream to simulate user input during the test.
  2. Restoring System.in: Ensure that System.in is restored to its original state after the test to avoid side effects on other tests.
  3. Writing Assertions: Verify the behavior of the code by checking the output or state after processing the input.

Working of System.in Unit Testing

We can replace System.in with any input stream using the System.setIn(InputStream in) method. For testing purposes, ByteArrayInputStream can be used to simulate the input. After simulating the input, we can execute the method under test and make assertions to verify that the method behaves as expected.

Example:

Create a Method that Reads from System.in

Let’s create a simple Java class, InputReader, with a method that reads user input from System.in and returns it.

public class InputReader {

// This method reads from System.in and returns the input as a String
public String readInput() {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter input: ");
return scanner.nextLine();
}
}

In this class, readInput() reads a line of input from System.in and returns it. Normally, this method would wait for user input, but for testing purposes, we will simulate this input.

Simulating System.in for Testing

To test this method without requiring user interaction, we’ll use ByteArrayInputStream to simulate System.in. By setting System.in to an input stream containing test data, we can automate the input for testing purposes.

Write the JUnit Test Case

Here is a JUnit test for the InputReader class. We will set System.in to a ByteArrayInputStream that contains our simulated user input.

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class InputReaderTest {

private final InputStream originalSystemIn = System.in;
private ByteArrayInputStream testIn;

@BeforeEach
public void setUpInput() {
// This method runs before each test to set up the input
}

@AfterEach
public void restoreSystemIn() {
// Restore System.in after each test
System.setIn(originalSystemIn);
}

@Test
public void testReadInput() {
// Simulate user input "Hello World"
String simulatedInput = "Hello World";
testIn = new ByteArrayInputStream(simulatedInput.getBytes());
System.setIn(testIn);

// Invoke the method to be tested
InputReader inputReader = new InputReader();
String input = inputReader.readInput();

// Assert that the method returns the correct result
assertEquals(simulatedInput, input);
}
}

Project Implementation of System.in Unit Testing With JUnit

Step 1: Create a New Maven Project

Create a new Maven project using IntelliJ IDEA. Choose the following options:

  • Name: testing-system-in
  • Build system: Maven

Click on the Create button.

Project Metadata

Project Structure

After the project creation done, set the folder structure as shown in the below image:

Project Folder Structure

Step 2: Add the JUnit Dependencies to pom.xml

Open the pom.xml file and add the following JUnit 5 dependencies:

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gfg</groupId>
    <artifactId>testing-system-in</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- JUnit 5 Dependency -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>

</project>

Step 3: Create the UserInput Class

Create a class named UserInput to read a number from the user and return its square:

Java
package com.gfg;

import java.util.Scanner;

public class UserInput {

    // Method to read a number from the user and return its square
    public int readNumberAndSquare() {
        Scanner scanner = new Scanner(System.in);  // Read from System.in
        int number = scanner.nextInt();  // Read an integer from the user
        return number * number;  // Return the square of the number
    }
}

Step 4: Create the Main Class

Create a main class to execute the program:

Java
package com.gfg;

public class Main {
    public static void main(String[] args) {
        UserInput userInput = new UserInput();  // Create an instance of the class

        System.out.println("Please enter a number:");  // Prompt the user for input

        int result = userInput.readNumberAndSquare();  // Call the method to get the square of the number

        System.out.println("The square of the entered number is: " + result);  // Print the result
    }
}

Step 5: Create the UserInputTest Class

To test the UserInput class, create the following test class:

Java
import com.gfg.UserInput;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class UserInputTest {

    @Test
    public void testReadNumberAndSquare() {
        // Store the original System.in for later restoration
        InputStream originalIn = System.in;

        try {
            // Simulate input for System.in (e.g., user inputs "5")
            String input = "5";
            ByteArrayInputStream in = new ByteArrayInputStream(input.getBytes());
            System.setIn(in);  // Redirect System.in to use our simulated input

            // Create an instance of the class and call the method
            UserInput userInput = new UserInput();
            int result = userInput.readNumberAndSquare();

            // Assert that the square of 5 is 25
            assertEquals(25, result);

        } finally {
            // Restore the original System.in
            System.setIn(originalIn);
        }
    }
}

Step 6: Run the Application

Once the project is complete, run the Main class. We will see the below output in the console:

Output

Step 7: Running the Tests

Now, we will run the tests, use the following Maven command in the terminal.

mvn test

Output:

Test Output

In this article, we demonstrated how to perform unit testing on methods that read from System.in using JUnit.

Conclusion

Simulating System.in in the unit tests is a powerful technique to test the input-dependent methods in the Java. By redirecting the input stream using the ByteArrayInputStream, we can simulate the various user inputs and verify the correctness of the methods. This approach can helps in the automating tests and ensuring the code quality.


Next Article
Article Tags :

Similar Reads