Как проверить уровень доступа к данным?

17

У меня есть метод DAO, который использует Spring для доступа JDBC. Он рассчитывает показатель успешности продавца при продаже предмета.

Вот код:

public BigDecimal getSellingSuccessRate(long seller_id) {
    String sql = "SELECT SUM(IF(sold_price IS NOT NULL, 1, 0))/SUM(1) 
                  FROM transaction WHERE seller_id = ?";
    Object[] args = {seller_id};
    return getJdbcTemplate().queryForObject(sql, args, BigDecimal.class);
}

Как мне пройти тестирование этого метода или любого метода DAO с помощью JUnit? Каковы некоторые рекомендации по проверке логики доступа к данным? Я думаю о том, чтобы протестировать его на встраиваемой базе данных, загруженной некоторыми данными, но разве мы не должны проводить интеграционные тесты, аналогичные производственной среде, с точки зрения СУБД и схемы?

Майкл
источник
Проверьте DBUnit . Это сделано специально для решения вашей проблемы.
Серхио,

Ответы:

15

Проблема с использованием «реальной» базы данных для модульного тестирования заключается в установке, отключении и изоляции тестов. Вам не нужно раскручивать совершенно новую базу данных MySQL и создавать таблицы и данные только для одного модульного теста. Проблемы с этим связаны с внешним характером базы данных, и ваша тестовая база данных не работает, ваши модульные тесты не пройдены. Есть также проблемы с проверкой наличия уникальной базы данных для тестирования. Их можно преодолеть, но есть более простой ответ.

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

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

db.url=jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;

Это загружает состояние базы данных (таблицы, исходные данные) из testDataфайла. shutdown=trueавтоматически отключит базу данных при закрытии последнего соединения.

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

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

Модульные тесты будут выглядеть примерно так (куча скучных вещей для краткости не включена):

    @Before
    public void setUpDB() {
        DBConnection connection = new DBConnection();
        try {
            conn = connection.getDBConnection();
            insert = conn.prepareStatement("INSERT INTO data (txt, ts, active) VALUES (?, ?, ?)");
        } catch (SQLException e) {
            e.printStackTrace();
            fail("Error instantiating database table: " + e.getMessage());
        }
    }

    @After
    public void tearDown() {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private void addData(String txt, Timestamp ts, boolean active) throws Exception {
        insert.setString(1, txt);
        insert.setTimestamp(2, ts);
        insert.setBoolean(3, active);
        insert.execute();
    }

    @Test
    public void testGetData() throws Exception {
        // load data
        Calendar time = Calendar.getInstance();
        long now = time.getTimeInMillis();
        long then1h = now - (60 * 60 * 1000);  // one hour ago
        long then2m = now - (60 * 1000 * 2);   // two minutes ago
        addData("active_foo", new Timestamp(then1h), true);     // active but old
        addData("inactive_bar", new Timestamp(then1h), false);  // inactive and old
        addData("active_quz", new Timestamp(then2m), true);     // active and new
        addData("inactive_baz", new Timestamp(then2m), false);  // inactive and new

        DataAccess dao = new DataAccess();
        int count = 0;
        for (Data data : dao.getData()) {
            count++;
            assertTrue(data.getTxt().startsWith("active"));
        }

        assertEquals("got back " + count + " rows instead of 1", count, 1);
    }

И, таким образом, у вас есть модульный тест, который вызывает DAO и использует данные, которые были настроены в оперативной базе данных, которая существует на время теста. Вам не нужно беспокоиться о внешних ресурсах или состоянии базы данных перед запуском или восстановлением известного состояния (ну, «известное состояние» - это «не существует», к которому тривиально вернуться).

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

Приведенный выше код является частью проекта maven, который я написал для доказательства концепции TestingWithHsqldb на github.


источник
2
Я не знал о том, что HSQL может издеваться над диалектом другого производителя БД. Спасибо.
Майкл
1
@ Соберите это через свойства базы данных, например, sql.syntax_mys=trueкоторые изменяют способ работы hsqldb: «Когда установлено это значение, это свойство включает поддержку типов TEXT и AUTO_INCREMENT, а также обеспечивает совместимость с некоторыми другими аспектами этого диалекта». в то время sql.syntax_ora=trueкак «Это свойство, если оно установлено в true, включает поддержку нестандартных типов. Оно также включает синтаксис DUAL, ROWNUM, NEXTVAL и CURRVAL, а также обеспечивает совместимость с некоторыми другими аспектами этого диалекта».
DBUnit это путь :)
Сильвиу Бурча
@SilviuBurcea DBUnit, безусловно, значительно упрощает настройку сложной среды тестирования базы данных, чем выполнение ее вручную. Иногда полезно знать, как это сделать вручную, если вам нужно (упомянутый выше подход «вручную» может быть перенесен на другие языки, где DBUnit не подходит).
Вы можете посмотреть на чтец
cchantep
2

Во-первых, вы никогда не должны проводить тестирование в производственной среде. У вас должна быть тестовая среда, которая отражает вашу производственную среду и проводить там интеграционные тесты.

Если вы это сделаете, то вы можете сделать несколько вещей.

  • Напишите модульные тесты, которые проверяют, передается ли соответствующий SQL-элемент фиктивному элементу с использованием фиктивной инфраструктуры, такой как Mockito. Это гарантирует, что ваш метод делает то, что он должен делать, и исключает интеграцию из картинки.
  • Напишите тестовые сценарии SQL, демонстрирующие соответствие SQL, который вы тестировали в своих модульных тестах. Это может помочь с любыми проблемами настройки, с которыми вы можете столкнуться, поскольку вы также можете запускать объяснения и тому подобное на основе ваших тестовых сценариев.
  • Используйте DBUnit, как упомянуто @Sergio.
Мэтью Флинн
источник
Вупс, когда я сказал производственную среду, я имел в виду ее симуляцию. Спасибо за ваш ответ, я посмотрю на Mockito, потому что это то, что я тоже хотел узнать.
Майкл
1

В нашем проекте каждый разработчик работает с пустой базой данных, ее структура совпадает с производственной базой данных.

В каждом модульном тесте TestInitialize мы создаем соединение и транзакцию с базой данных, а также некоторые объекты по умолчанию, которые нам нужны для каждого теста. И все откатывается после окончания каждого метода или класса.

Таким образом, можно протестировать слой sql. Фактически, каждый запрос или вызов базы данных должен быть проверен таким образом.

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

Карра
источник
Если вы используете базу данных в памяти, то вместо выполнения транзакции отката можно использовать подход «создание-откат» до запуска всех наборов тестов, что намного быстрее.
Даунхиллски,
Никогда не думал, что так будет раньше. В наших тестах большинство тестов создают пользователя 'x', хотя он уникален. Создание БД один раз означало бы изменение тестов для повторного использования этих объектов.
Карра
Я знаю, мы на одной странице, и мне нравится ваш подход. Ваш подход гарантирует, что каждый тест может быть независимо запущен независимо от порядка, и каждый раз перед его запуском состояние таблицы данных одинаково.
Даунхиллски
Это правильно, порядок не имеет значения тогда. Ранее мы видели, как тесты не выполнялись, потому что порядок запуска модульных тестов на нашем сборочном компьютере и локальном компьютере отличается.
Карра