0

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!

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
IMWBY
  • 1
  • "When unit testing in IntelliJ IDEA Education Edition, the console does not support System.in input" - Is that definitely true? That does not sound right. – Jeff Scott Brown Apr 08 '22 at 13:30
  • Does this answer your question? [JUnit: How to simulate System.in testing?](https://stackoverflow.com/questions/1647907/junit-how-to-simulate-system-in-testing) – Meo Apr 08 '22 at 16:36
  • Normally tests are supposed to be automatic and do not expect input from console. Please see https://youtrack.jetbrains.com/issue/IDEA-148698#comment=27-2257633 for a way to workaround this but please also note that there are side effects. – y.bedrov Apr 08 '22 at 19:47

1 Answers1

0

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.

Timothy Truckle
  • 15,071
  • 2
  • 27
  • 51