Подтвердить что ты не робот

Простой способ заполнить ResultSet данными

Я хочу высмеять ResultSet. Шутки в сторону. Я реорганизую один большой сложный фрагмент кода, который анализирует данные из ResultSet, и я хочу, чтобы мой код вел себя одинаково. Поэтому мне нужно написать unit test для рефакторинга, чтобы проверить это.

После googling я придумал 2 идеи:

  • Используйте EasyMock, напишите looooong mocking sequence. Решение VERY BAD: трудно добавлять исходные данные, трудно изменить данные, большие отладочные обещания для тестирования.
  • Используйте Apache Derby или HSQLDB для создания БД в памяти, заполните его из файла или массива String, запросите некоторые магические InMemoryDBUtils.query(sql). Затем используйте этот ResultSet. К сожалению, я не нашел волшебных InMemoryDBUtils, чтобы быстро написать тест:-). Статья IBM "Изолированное единичное тестирование настойчивости с Derby" кажется просто прекрасным в том, что мне нужно, хотя...

Второй подход выглядит несколько проще и гораздо более удобным.

Что бы вы посоветовали создать такой макет? (несмотря на врачей, конечно:-)? Я пропустил <ударить > бровь серебряную пулю? Возможно, DBUnit - это инструмент для этого?

4b9b3361

Ответ 1

DBUnit, насколько мне известно, не представляет набор результатов, хотя он хорошо поможет вам заполнить вашу базу данных в памяти.

Я бы сказал, что насмешливая структура - это неправильный подход на данный момент. Стыковка - это тестирование поведения и взаимодействия, а не просто возврат данных, поэтому он, скорее всего, будет мешать вам.

Вместо этого я либо реализую интерфейс набора результатов, либо создаю динамический прокси-интерфейс интерфейса результирующего набора для класса, который реализует методы, о которых вы заботитесь, без необходимости реализации всего набора результатов. Скорее всего, вы сохраните класс таким же простым, как поддержание базы данных в памяти (при условии, что тестируемый набор данных согласован) и, вероятно, легче отлаживать.

Вы можете создать резервную копию этого класса с помощью DBUnit, где вы делаете снимок вашего результирующего набора с помощью dbunit и dbunit читаете его во время теста из xml, и ваш фиктивный набор результатов считывает данные из классов dbunit. Это было бы разумным подходом, если бы данные были слегка сложными.

Я бы пошел в базу данных в памяти, если классы были настолько связаны, что им нужно было прочитать данные, которые были изменены как часть одного и того же теста. Даже тогда я хотел бы использовать копию реальной базы данных, пока вам не удастся отделить эту зависимость.

Простой способ генерации прокси:

private static class SimpleInvocationHandler implements InvocationHandler {
    private Object invokee;

    public SimpleInvocationHandler(Object invokee) {
        this.invokee = invokee;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        method = invokee.getClass().getMethod(method.getName(), method.getParameterTypes());
        if (!method.isAccessible()) {
            method.setAccessible(true);
        }
        try {
            return method.invoke(invokee, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }
}

public static <T> T generateProxy(Object realObject, Class... interfaces) {
    return (T) Proxy.newProxyInstance(realObject.getClass().getClassLoader(), interfaces, new SimpleInvocationHandler(realObject));
}

Ответ 2

У меня был успех с классом MockResultSet отсюда: http://mockrunner.sourceforge.net/. Он позволяет создать класс, который реализует интерфейс ResultSet, и позволяет устанавливать значения для каждого столбца и строки.

Если ваши методы работают с ResultSets разумного размера, вы должны иметь возможность создавать тесты, которые возвращают нужные вам значения довольно легко.

Вот простой пример:

MockResultSet rs = new MockResultSet("myMock");

rs.addColumn("columnA", new Integer[]{1});
rs.addColumn("columnB", new String[]{"Column B Value"});
rs.addColumn("columnC", new Double[]{2});

// make sure to move the cursor to the first row
try
{
  rs.next();
}
catch (SQLException sqle)
{
  fail("unable to move resultSet");
}

// process the result set
MyObject obj = processor.processResultSet(rs);

// run your tests using the ResultSet like you normally would
assertEquals(1, obj.getColumnAValue());
assertEquals("Column B Value", obj.getColumnBValue());
assertEquals(2.0d, obj.getColumnCValue());

Ответ 3

Mockrunner может загружать CSV или XML файл и автоматически создавать MockResultSet. Он также может высмеять Connection и Statement, поэтому все ваши материалы JDBC просто работают, даже не добавляя JDBC-драйвер в ваш путь к классам.

Ответ 4

Я написал что-то для этого же случая. Вы можете издеваться над набором результатов с помощью Mockito. Вы можете также петли над макетными строками набора результатов, высмеивая resultet.next() с помощью этого фрагмента кода.

// two dimensional array mocking the rows of database.
String[][] result = { { "column1", "column2" }, { "column1", "column2" } };

@InjectMocks
@Spy
private TestableClass testableClass;

@Mock
private Connection connection;

@Mock
private Statement statement;

@Mock
private ResultSet resultSet;

@BeforeTest
public void beforeTest() {
    MockitoAnnotations.initMocks(this);
}

@BeforeMethod
public void beforeMethod() throws SQLException {
    doAnswer(new Answer<Connection>() {
        public Connection answer(InvocationOnMock invocation)
                throws Throwable {
            return connection;

        }
    }).when(testableClass).getConnection();

    when(connection.createStatement()).thenReturn(statement);
    when(statement.executeQuery(anyString())).thenReturn(resultSet);
    final AtomicInteger idx = new AtomicInteger(0);
    final MockRow row = new MockRow();

    doAnswer(new Answer<Boolean>() {

        @Override
        public Boolean answer(InvocationOnMock invocation) throws Throwable {
            int index = idx.getAndIncrement();
            if (result.length > index) {
                String[] current = result[index];
                row.setCurrentRowData(current);
                return true;
            } else
                return false;

        }

        ;
    }).when(resultSet).next();

    doAnswer(new Answer<String>() {

        @Override
        public String answer(InvocationOnMock invocation) throws Throwable {
            Object[] args = invocation.getArguments();
            int idx = ((Integer) args[0]).intValue();
            return row.getColumn(idx);
        }

        ;
    }).when(resultSet).getString(anyInt());
}

static class MockRow {
    String[] rowData;

    public void setCurrentRowData(String[] rowData) {
        this.rowData = rowData;
    }

    public String getColumn(int idx) {
        return rowData[idx - 1];
    }
}

Ответ 5

Если применимо, вы можете взять набор результатов, который у вас есть сейчас, из вашего реального источника данных, сериализовать его и сохранить файл. Затем вы можете десериализовать этот результирующий набор для каждого из ваших модульных тестов, и вам должно быть хорошо идти.

Ответ 6

Пока вы не вызываете большинство методов ResultSet, я бы, вероятно, просто загрузил текстовый файл с разделителями в двумерный массив и реализовал нужные мне методы, оставив остальным бросить UnsupportedOperationException (который является реализацией по умолчанию для методов stubbed-out в моей среде IDE).