Search code examples
javainputjunitjunit3

JUnit testing layout


I have a specific dictionary project that takes in user inputs and writes them to a database. The inputs (for now) are taken in through scanners (System.in) and I have multiple cases for example:

  1. Enter word and definition
  2. Enter synonyms
  3. Print dictionary

Things like this. How can I replicate user inputs with JUnit?


Solution

  • GhostCat is right. I would like completing its answer.

    In your application, there are 2 distinct responsibilities :

    • capturing user requests for each one of your business cases(Enter word and definition, Enter synonyms, Print dictionary) : a user interface responsibility.
    • processing the logic : a logic responsibility.

    Good design tries to avoid mixing responsibilities. So, you should create a class for :

    • capturing user requests. It captures the user input from the System.in and it dispatches data to the appropriate method of the processing class.
    • processing the logic. it is the logic class.

    What is the advantage of it ?
    The logic of your application is now in the class processing the logic.
    In this way, you can concentrate unit test on this class to check that your application handle the logic as expected.

    With a design like it, You don't need to worry about the scanner since it's a mature component which works : you don't need to unit test it works.

    -- Edit : For answering your comment about how to realize an integration test with System.in

    I looked the code.
    First, with your actual code, you could not do an integration test with scanner which relies on multiple inputs captured because classic mocking tools as Mockito doesn't allow to do it (class is final).
    You can stub the inputstream but as in the same method, you take multiple inputs (option selection + values), you cannot stubbed the two inputs.
    So, we have to find a trick for bypassing it. A way to handle the problem is creating two ways of using your application : one with input entered one by one by user and another with all inputs entered in one time with delimiters for your unit test.

    All is not operational but you can follow the principle.

    The unit test will be like that :

    @Test
    public void printOptionWithAddedWordInDictionary() throws Exception {
      final ByteArrayInputStream inputStreamStubbed = new   ByteArrayInputStream("a|dog|super animal!".getBytes());
      InputReceiver inputReceiver = new InputReceiver(inputStreamStubbed, "\\|");
      inputReceiver.printOptions();
    }
    

    And as you see, you have two constructors now and two new private fields :

    private InputStream inputStream;
    private Scanner input;
    
    public InputReceiver() {
      inputStream = System.in;
      scanner = new Scanner(inputStream);
    }
    
      // for unit testing
     InputReceiver(InputStream inputStream, String delimiter) {
      this.inputStream = inputStream;
      scanner = new Scanner(inputStream);
      scanner.useDelimiter(delimiter);
    }
    

    The second is package private for testing.