Skip to content
CodeKruPro

Your language · Java

Files and input/output

Reading and writing files, paths, and reading test data.

Learn: Files and input/output in Java8 minute read

Input/output (I/O) means reading data into a program and writing data out, and in test code that usually means files. A framework reads configuration, test data and expected JSON (a common text format for structured data), and writes logs, screenshots and reports. Interviewers often ask you to "read a file line by line" or "read a properties file" (a text file of settings, explained in step 3), and the follow-up is usually why a data file is found on your laptop but not on the CI server (a server that runs your tests automatically).

Step 1: Path and Files

A java.nio.file.Path is a file location. Path.of("testdata", "users.csv") joins the parts with the right separator for the operating system. The Files class has static helpers for most jobs:

  • Files.readString(path) returns the whole file as one String.
  • Files.readAllLines(path) returns a List<String>, one entry per line.
  • Files.lines(path) returns a lazy stream of lines for big files (a stream reads lines only when needed; see Lambdas and streams); close it with try-with-resources.
  • Files.newBufferedReader(path) gives a reader for a line-by-line loop.
  • Files.writeString, Files.exists, Files.createDirectories, Files.copy and Files.delete do the rest.

These methods use UTF-8, a text encoding that covers every language, unless you pass another one; since Java 18 UTF-8 is also the JVM's default. A missing file gives NoSuchFileException, while the older FileReader gives FileNotFoundException. Both extend IOException, which is checked, so you must catch it or declare it with throws (see Exceptions).

Step 2: reading line by line

The classic answer uses a BufferedReader (a reader that reads text efficiently) inside try-with-resources (a try that closes things for you at the end), so the file is closed even if something fails. readLine() returns null at the end of the file.

import java.io.*;
import java.nio.file.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        Path file = Path.of("users.csv");
        Files.writeString(file, "name,role\nAsha,admin\n\nRavi,viewer\n");

        try (BufferedReader reader = Files.newBufferedReader(file)) {
            String line;
            int number = 0;
            while ((line = reader.readLine()) != null) {
                number++;
                if (line.isBlank() || number == 1) continue;   // skip header and blanks
                String[] parts = line.split(",");
                System.out.println(parts[0] + " is " + parts[1]);
            }
        }

        List<String> lines = Files.readAllLines(file);
        System.out.println(lines.size() + " lines");

        Files.writeString(file, "extra\n", StandardOpenOption.APPEND);
        System.out.println(Files.readAllLines(file).get(4));

        try {
            Files.readString(Path.of("missing.txt"));
        } catch (NoSuchFileException e) {
            System.out.println("missing: " + e.getMessage());
        }
    }
}
Asha is admin
Ravi is viewer
4 lines
extra
missing: missing.txt

Files.writeString replaces a file's content by default, and StandardOpenOption.APPEND adds to the end. Splitting on commas works only for simple data. CSV (comma-separated values) files can hold quoted commas, so a framework should use a CSV library.

Step 3: properties files

Settings are often kept as key=value lines. java.util.Properties loads them: lines that start with # are comments and spaces around = are ignored. Every value is a String, and getProperty returns null for a missing key unless you give a default.

import java.io.*;
import java.nio.file.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        Path cfg = Path.of("config.properties");
        Files.writeString(cfg, "# test settings\nbrowser = chrome\ntimeout=15\n");

        Properties props = new Properties();
        try (Reader reader = Files.newBufferedReader(cfg)) {
            props.load(reader);
        }
        String browser = System.getProperty("browser", props.getProperty("browser"));
        int timeout = Integer.parseInt(props.getProperty("timeout", "10"));
        System.out.println(browser + " " + timeout + " " + props.getProperty("baseUrl"));
    }
}
chrome 15 null

The System.getProperty line lets a CI job override the file: you add -Dbrowser=firefox to the command that starts the tests.

Step 4: the working directory and the classpath

A relative path (one that does not start at the drive or root, such as testdata/users.csv) is resolved against the JVM's working directory (the folder the program was started from), System.getProperty("user.dir"), which can differ between your IDE and the CI server. That is the most common reason a data file is "not found" only in CI. Maven and Gradle copy files under src/test/resources onto the test classpath (the set of places Java loads classes and resources from), so reading them as resources works from any folder:

try (InputStream in = getClass().getResourceAsStream("/testdata/users.json")) {
    User[] users = new ObjectMapper().readValue(in, User[].class);
}

How to answer in an interview

Write the try-with-resources loop with BufferedReader, then say why each part is there: "try-with-resources closes the file even on an exception, and readLine returns null at the end." For test data, name the format and the library: Properties for settings, Jackson (a JSON library) for JSON, Apache POI (a library for Excel files) for Excel, a CSV library for CSV, all read from the classpath and fed to a TestNG @DataProvider (a method that gives a test several rows of data, so the test runs once per row).

Common mistakes

  • Opening readers without try-with-resources, which leaks file handles (the file stays open and uses up the limited number of files a program may open).
  • Hard-coding absolute paths such as C:\Users\me\data.csv.
  • Expecting getProperty to return a number; every value is text.
  • Forgetting that writeString replaces the file unless you pass APPEND.
  • Keeping real passwords in committed files instead of environment variables.

Recap

Use Path and Files for most file work, and try-with-resources for every reader. Read small files whole and big ones line by line. Properties files hold text values with optional defaults. Relative paths depend on the working directory, so read test data from the classpath.

8 questions0/8 solved

Not startedNot startedIn progressIn progressSolvedSolvedPro+Pro+

The first 2 questions of each topic are free. Pro+ opens every question.

Back to the plan