-1

I have a method that scans a file and assigns values to an object:

private List<Customer> customerList = new ArrayList<Customer>();

public void scanLocalFile() throws FileNotFoundException {
  File file = new File("input.txt");
  Scanner scan = new Scanner(file);
  while (scan.hasNextLine()) {
      String[] fields = line.split(";");
      String name = fields[0];
      String score = fields[1];

  Customer customer = new Customer(name, score);
  customerList.add(customer);
  }
scan.close();
}

How do I write a junit test for a method like this? Is it possible to use the same file ("input.txt") for a unit test as well?

The file's structure:

John Smith;45;
Adam West;78;
dennis
  • 19
  • 5
  • 1
    don't hardcode the file; make sure you can pass it as a parameter to that method. then call that method with a test input file, and check the content of customerList afterwards – Stultuske Sep 22 '21 at 08:12
  • 1
    Here is similar question [how-to-junit-test-a-method-with-a-scanner](https://stackoverflow.com/questions/34139658/how-to-junit-test-a-method-with-a-scanner/34139879). – Sangeerththan Balachandran Sep 22 '21 at 08:14
  • 1
    Or, pass in a `Reader` which you use as a parameter to the `Scanner` constructor: then you don't even have to worry about files, as you can use e.g. `StringReader`. Note, however, that if you pass in a `Reader`, you shouldn't close the `Scanner` at the end, because that will close the `Reader`; and it is bad practice for code to close a `Reader` (or `Writer` or `Input/OutputStream`) that it didn't open. – Andy Turner Sep 22 '21 at 08:15
  • 1
    Also also: it's a bit dubious to have `customerList` as a field that you add to (what if you invoke `scanLocalFile()` twice?). Construct a new list in this method, add to it, and return the list. – Andy Turner Sep 22 '21 at 08:17

1 Answers1

1

The basic approach to make this code unit-testable is to not hard-code the use of the input.txt file.

Stultuske suggests passing in the File as a parameter to the method, in order that you can then run the method for an arbitrary file during testing.

I'd go a step further than that and say: pass in a Reader. Scanner has a constructor which accepts a Reader, and then you don't have to mess around with files during you unit test.

For example, you can construct a StringReader with a hard-coded string that is the "file contents".

StringReader sr = new StringReader("John Smith;45;\nAdam West;78;");

Note, however, that if you pass in a Reader, you shouldn't close the Scanner at the end, because that will close the Reader; and it is bad practice for code to close a Reader (or Writer or Input/OutputStream; generally any resource) that it didn't open.

public void scanLocalFile(Reader r) {
  Scanner scan = new Scanner(r);
  while (scan.hasNextLine()) {
     // ...
  }
}

Actually, since you're just reading lines from the Scanner, I think it would be better to use BufferedReader instead:

public void scanLocalFile(Reader r) {
  new BufferedReader(r).lines()
      .map(line -> {
        String[] fields = line.split(";");
        return new Customer(fields[0], fields[1]);
      })
      .forEach(customerList::add);
}
Andy Turner
  • 137,514
  • 11
  • 162
  • 243