Як перевірити рівень доступу до даних?


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? Які найкращі практики для перевірки логіки доступу до даних? Я думаю про тестування його на вбудованій базі даних, завантаженій деякими даними, але чи не слід робити інтеграційні тести, подібні виробничому середовищу з точки зору RDBMS та схеми?


Перевірте 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 і використовує дані, встановлені в базі даних fly, яка існує протягом тривалості тесту. Вам не доведеться турбуватися про зовнішні ресурси або стан бази даних перед запуском або відновлення відомого стану (ну, "відомий стан" є "не існує", що тривіально для повернення).

DBUnit може зробити більшу частину того, що я описав, більш простим процесом у створенні бази даних, створенні таблиць та завантаженні даних. Якщо вам знадобиться чомусь використовувати фактичну базу даних, це набагато кращий інструмент для використання.

Вищевказаний код є частиною проекту Maven, який я написав для підтвердження концепції TestingWithHsqldb на github


2
Я не знав про частину того, що HSQL може знущатися з діалекту іншого постачальника db. Дякую.
Майкл

1
@Dog це можна зробити за допомогою властивостей бази даних, таких як sql.syntax_mys=trueзміна способу роботи hsqldb: "Ця властивість, коли встановлено значення true, підтримує типи TEXT та AUTO_INCREMENT, а також дозволяє сумісність з деякими іншими аспектами цього діалекту." у той час як sql.syntax_ora=true"Ця властивість, коли встановлено значення true, дозволяє підтримувати нестандартні типи. Вона також включає синтаксис DUAL, ROWNUM, NEXTVAL та CURRVAL, а також дозволяє сумісність з деякими іншими аспектами цього діалекту."

DBUnit - це шлях :)
Silviu Burcea

@SilviuBurcea DBUnit, безумовно, робить набагато простішим "сантехнічне" створення складного середовища тестування баз даних набагато простіше, ніж робити це вручну. Іноді все ще корисно знати, як це зробити вручну, якщо вам потрібно (підхід "від руки", згаданий вище, може бути перенесений на інші мови, де DBUnit не є можливим).

Ви можете подивитися на читець
cchantep

2

По-перше, ви ніколи не повинні робити тестування у виробничих умовах. У вас повинно бути тестове середовище, яке відображає ваше виробниче середовище і робити там інтеграційні тести.

Якщо ви це зробите, то можете зробити ряд речей.

  • Напишіть тестові одиниці, які тестують, щоб переконатися, що відповідний SQL подається на макетний елемент, використовуючи глузливі рамки, такі як Mockito. Це забезпечить, що ваш метод робить те, що належить зробити, і виведе інтеграцію з картини.
  • Напишіть тестові сценарії SQL, що демонструють відповідність тестуваного SQL у ваших одиничних тестах. Це може допомогти з будь-якими проблемами налаштування, з якими ви можете зіткнутися, оскільки ви також можете запускати пояснення та подібне на основі тестових сценаріїв.
  • Використовуйте DBUnit, як згадував @Sergio.

Вуп, коли я сказав, що виробниче середовище насправді маю на увазі симуляцію цього. Дякую за вашу відповідь, я погляну на Mockito, тому що це я теж хотів дізнатися.
Майкл

1

У нашому проекті кожен розробник працює з порожньою базою даних, її структура така ж, як і виробнича база даних.

У кожному блоці тестів TestInitialize ми створюємо підключення та транзакцію до бази даних, а також деякі об'єкти за замовчуванням, які нам потрібні для кожного тесту. І все згортається після закінчення кожного методу чи класу.

Таким чином можна протестувати рівень sql. Насправді кожен запит або виклик до бази даних повинні бути протестовані таким чином.

Мінус у тому, що він повільний, тому ми ставимо його в окремий проект з наших звичайних одиничних тестів. Це можна прискорити за допомогою бази даних в пам'яті, але ідея залишається такою ж.


Якщо ви використовуєте базу даних в пам'яті, тоді замість трансляції відкату можна застосувати підхід для створення крапель до запуску всіх тестових наборів, що просто набагато швидше.
Downhillski

Ніколи раніше не думав робити це так. У наших тестах більшість тестів створюють користувачеві "x", хоча це унікально. Створити db один раз означатиме зміну тестів для повторного використання цих об'єктів.
Карра

Я знаю, ми на одній сторінці і мені подобається ваш підхід. ваш підхід гарантує, що кожен тестовий випадок можна самостійно запускати незалежно від порядку, і кожен раз перед його запуском стан таблиці даних є однаковим.
Downhillski

Це правильно, порядок тоді не має значення. Ми вже бачили тести, які не вийшли з ладу, тому що порядок запуску тестових одиниць відрізняється на нашому ПК та локальній машині.
Carra
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.