Search code examples
javamockitoopencsv

Testing for exceptions in Opencsv CSVReader


I am trying to test exception handling when using Opencsv's CSVReader. The data will be coming from a String. It's not working because I am (probably) not properly mocking the CSVReader, but cannot quite figure out what I need to do.

Here's the class

import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.exceptions.CsvValidationException;
// other imports skipped

public class MyCsvReader {
    private Path contentsAsString;
    private CSVReader csvReader;

    public MyCsvReader(final String contentsAsString) {
        InputStream inputStream = new ByteArrayInputStream(contentsAsString.getBytes());
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);

        csvReader = new CSVReaderBuilder(inputStreamReader)
                .withSkipLines(0)
                .withKeepCarriageReturn(false)
                .build();
    }

    public void readData() {
        String[] line;

        try {
            while ((line = csvReader.readNext()) != null) {
                System.out.println("line:" + Arrays.toString(line));
            }
        } catch (IOException e) {
            System.out.println("got IOException");
            // I will be throwing a custom exception here
            throw new RuntimeException(e);
        } catch (CsvValidationException e) {
            System.out.println("got CsvValidationException");
            // and a different custom exception here
            throw new RuntimeException(e);
        }
    }
}

and the test

public class MyCsvReaderTest {

    @Test
    public void testException() throws Exception {
        String[] rows = {
                "column1,column2,column3",
                "test,abc,def"
        };
        String rowData = String.join("\n", rows);

        CSVReader mock = Mockito.mock(CSVReader.class);
        Mockito.when(mock.readNext()).thenThrow(new IOException("test"));

        MyCsvReader reader = new MyCsvReader(rowData);
        try {
            reader.readData();
            fail("Expected an exception, but call succeeded");
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
    }
}

When I run it, reader.readNext() does not throw an exception

line: [column1, column2, column3]
line: [test, abc, def]

org.opentest4j.AssertionFailedError: Expected and exception, but call succeeded
... stack trace deleted

Suggestions on what I need to do? Thanks!


Solution

  • You don't use a mocked instance but a real one. If the goal here is throw an exception when mock.readNext() happens you have to do something like this in order to inject your mocked instance of CSVReader:

    public class MyCsvReader {
        private final CSVReader csvReader;
    
        public MyCsvReader(final CSVReader csvReader) {
            this.csvReader = csvReader;
        }
    
        public MyCsvReader(final String contentsAsString) {
            this(getCsvReader(contentsAsString));
        }
    
        private static CSVReader getCsvReader(final String contentsAsString) {
            InputStream inputStream = new ByteArrayInputStream(contentsAsString.getBytes());
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
            return new CSVReaderBuilder(inputStreamReader)
                .withSkipLines(0)
                .withKeepCarriageReturn(false)
                .build();
        }
    
        public void readData() {
            String[] line;
    
            try {
                while ((line = csvReader.readNext()) != null) {
                    System.out.println("line:" + Arrays.toString(line));
                }
            } catch (IOException e) {
                System.out.println("got IOException");
                // I will be throwing a custom exception here
                throw new RuntimeException(e);
            } catch (CsvValidationException e) {
                System.out.println("got CsvValidationException");
                // and a different custom exception here
                throw new RuntimeException(e);
            }
        }
    }
    
    public class MyCsvReaderTest {
        @Test
        public void testException() throws Exception {
            CSVReader mock = Mockito.mock(CSVReader.class);
            Mockito.when(mock.readNext()).thenThrow(new IOException("test"));
    
            MyCsvReader reader = new MyCsvReader(mock);
            try {
                reader.readData();
                fail("Expected an exception, but call succeeded");
            } catch (RuntimeException ignored) {
            }
        }
    }