0
Follow
2
View

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

darkplume 注册会员
2023-01-25 01:40

When doing unittest you have two components: The itself and the (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.

About the Author

Question Info

Publish Time
2023-01-25 01:39
Update Time
2023-01-25 01:39