Одна из приятных возможностей JUnit 4 — это параметризованные тесты, которые позволяют проводить тестирование на основе данных в JUnit с минимальными усилиями. Это достаточно просто и очень полезно для настройки базовых тестов, управляемых данными, определяя ваши тестовые данные непосредственно в вашем классе Java. Но что, если вы хотите получить свои тестовые данные откуда-то еще? В этой статье мы рассмотрим, как получить тестовые данные из электронной таблицы Excel.
Параметризованные тесты позволяют проводить тесты на основе данных в JUnit. То есть вместо того, чтобы использовать разные тестовые наборы, которые исследуют различные аспекты поведения вашего класса (или вашего приложения), вы определяете наборы входных параметров и ожидаемых результатов и тестируете, как ваше приложение (или, чаще всего, один конкретный компонент) ведет себя , Тесты на основе данных отлично подходят для приложений, в которых используются вычисления, для диапазонов тестирования, граничных условий и угловых случаев.
В JUnit типичный параметризованный тест может выглядеть так:
@RunWith(Parameterized.class)
public class PremiumTweetsServiceTest {
private int numberOfTweets;
private double expectedFee;
@Parameters
public static Collection data() {
return Arrays.asList(new Object[][] { { 0, 0.00 }, { 50, 5.00 },
{ 99, 9.90 }, { 100, 10.00 }, { 101, 10.08 }, { 200, 18},
{ 499, 41.92 }, { 500, 42 }, { 501, 42.05 }, { 1000, 67 },
{ 10000, 517 }, });
}
public PremiumTweetsServiceTest(int numberOfTweets, double expectedFee) {
super();
this.numberOfTweets = numberOfTweets;
this.expectedFee = expectedFee;
}
@Test
public void shouldCalculateCorrectFee() {
PremiumTweetsService premiumTweetsService = new PremiumTweetsService();
double calculatedFees = premiumTweetsService.calculateFeesDue(numberOfTweets);
assertThat(calculatedFees, is(expectedFee));
}
}
Тестовый класс имеет переменные-члены, которые соответствуют входным значениям (numberOfTweets) и ожидаемым результатам (ОжидаемыйFee).
@RunWith (Parameterzed.class) аннотация получает JUnit вводить тестовые данные в экземпляры тестового класса с помощью конструктора.
Тестовые данные предоставляются методом с аннотацией @Parameters . Этот метод должен возвращать коллекцию массивов, но помимо этого вы можете реализовать его так, как хотите. В приведенном выше примере мы просто создаем встроенный массив в коде Java. Тем не менее, вы также можете получить его из других источников. Чтобы проиллюстрировать этот момент, я написал простой класс, который читает электронную таблицу Excel и предоставляет данные в ней в следующей форме:
@RunWith(Parameterized.class) public class DataDrivenTestsWithSpreadsheetTest { private double a; private double b; private double aTimesB; @Parameters public static Collection spreadsheetData() throws IOException { InputStream spreadsheet = new FileInputStream("src/test/resources/aTimesB.xls"); return new SpreadsheetData(spreadsheet).getData(); } public DataDrivenTestsWithSpreadsheetTest(double a, double b, double aTimesB) { super(); this.a = a; this.b = b; this.aTimesB = aTimesB; } @Test public void shouldCalculateATimesB() { double calculatedValue = a * b; assertThat(calculatedValue, is(aTimesB)); } }
Электронная таблица Excel содержит таблицы умножения в трех столбцах:
Класс SpreadsheetData использует проект POI Apache для загрузки данных из электронной таблицы Excel и преобразования ее в список массивов объектов, совместимых с аннотацией @Parameters . Я поместил исходный код вместе с примерами модульного тестирования на BitBucket . Для любопытных класс SpreadsheetData показан здесь:
public class SpreadsheetData { private transient Collection data = null; public SpreadsheetData(final InputStream excelInputStream) throws IOException { this.data = loadFromSpreadsheet(excelInputStream); } public Collection getData() { return data; } private Collection loadFromSpreadsheet(final InputStream excelFile) throws IOException { HSSFWorkbook workbook = new HSSFWorkbook(excelFile); data = new ArrayList(); Sheet sheet = workbook.getSheetAt(0); int numberOfColumns = countNonEmptyColumns(sheet); List rows = new ArrayList(); List rowData = new ArrayList(); for (Row row : sheet) { if (isEmpty(row)) { break; } else { rowData.clear(); for (int column = 0; column < numberOfColumns; column++) { Cell cell = row.getCell(column); rowData.add(objectFrom(workbook, cell)); } rows.add(rowData.toArray()); } } return rows; } private boolean isEmpty(final Row row) { Cell firstCell = row.getCell(0); boolean rowIsEmpty = (firstCell == null) || (firstCell.getCellType() == Cell.CELL_TYPE_BLANK); return rowIsEmpty; } /** * Count the number of columns, using the number of non-empty cells in the * first row. */ private int countNonEmptyColumns(final Sheet sheet) { Row firstRow = sheet.getRow(0); return firstEmptyCellPosition(firstRow); } private int firstEmptyCellPosition(final Row cells) { int columnCount = 0; for (Cell cell : cells) { if (cell.getCellType() == Cell.CELL_TYPE_BLANK) { break; } columnCount++; } return columnCount; } private Object objectFrom(final HSSFWorkbook workbook, final Cell cell) { Object cellValue = null; if (cell.getCellType() == Cell.CELL_TYPE_STRING) { cellValue = cell.getRichStringCellValue().getString(); } else if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) { cellValue = getNumericCellValue(cell); } else if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) { cellValue = cell.getBooleanCellValue(); } else if (cell.getCellType() ==Cell.CELL_TYPE_FORMULA) { cellValue = evaluateCellFormula(workbook, cell); } return cellValue; } private Object getNumericCellValue(final Cell cell) { Object cellValue; if (DateUtil.isCellDateFormatted(cell)) { cellValue = new Date(cell.getDateCellValue().getTime()); } else { cellValue = cell.getNumericCellValue(); } return cellValue; } private Object evaluateCellFormula(final HSSFWorkbook workbook, final Cell cell) { FormulaEvaluator evaluator = workbook.getCreationHelper() .createFormulaEvaluator(); CellValue cellValue = evaluator.evaluate(cell); Object result = null; if (cellValue.getCellType() == Cell.CELL_TYPE_BOOLEAN) { result = cellValue.getBooleanValue(); } else if (cellValue.getCellType() == Cell.CELL_TYPE_NUMERIC) { result = cellValue.getNumberValue(); } else if (cellValue.getCellType() == Cell.CELL_TYPE_STRING) { result = cellValue.getStringValue(); } return result; } }
Тестирование на основе данных — отличный способ более тщательно протестировать приложения, основанные на вычислениях. В реальных приложениях эта электронная таблица Excel может быть предоставлена клиентом или конечным пользователем с бизнес-логикой, закодированной в электронной таблице. (Библиотека POI отлично справляется с числовыми вычислениями, хотя, похоже, у нее немного проблем с вычислениями по датам). В этом сценарии электронная таблица Excel становится частью ваших приемочных тестов и помогает определить ваши требования, позволяет эффективно разрабатывать сам код на основе тестов, а также действует как часть ваших приемочных тестов.