Одна из приятных возможностей 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 становится частью ваших приемочных тестов и помогает определить ваши требования, позволяет эффективно разрабатывать сам код на основе тестов, а также действует как часть ваших приемочных тестов.