Як імітувати БД для тестування (Java)?


76

Я програмую на Java, і мої додатки багато використовують БД. Отже, для мене важливо мати можливість легко перевірити використання БД.
Що це за тести БД? Для мене вони повинні забезпечити дві прості вимоги:

  1. Перевірте синтаксис SQL.
  2. Що ще важливіше, переконайтесь, що дані обрані / оновлені / вставлені правильно, відповідно до певної ситуації.

Ну, здається, що мені потрібна лише БД.
Але насправді я віддаю перевагу ні, оскільки є небагато труднощів із використанням БД для тесту:

  • "Просто придбайте собі тестову БД, наскільки це може бути важко?" - Ну, на моєму робочому місці мати персональне тестування БД досить неможливо. Ви повинні використовувати "загальнодоступну" БД, доступну для всіх.
  • "Ці тести не є швидкими ..." - Тести DB, як правило, повільніші, ніж звичайні тести. Насправді не ідеально мати повільні тести.
  • "Ця програма повинна розглядати будь-які випадки!" - Це стає дещо надокучливим і навіть неможливим намагатися змоделювати кожен випадок у БД. Для кожного випадку слід робити певну кількість запитів на вставку / оновлення, що дратує і вимагає часу.
  • "Зачекайте секунду, звідки ви знаєте, що в цій таблиці є 542 рядки?" - Одним з основних принципів тестування є можливість перевірити функціональність, відмінну від функції тестованого коду. Застосовуючи БД, зазвичай є один спосіб зробити щось, тому тест точно такий же, як і основний код.

Отже, ви можете зрозуміти, що мені не подобаються БД, коли справа доходить до тестів (звичайно, мені доведеться дістатися до цього в якийсь момент, але я волів би дістатись пізніше під час тестування, після того, як я знайшов більшість помилок за допомогою решта методів випробувань). Але що я шукаю?

Я шукаю спосіб імітувати БД, фальшиву БД, використовуючи файлову систему або просто віртуальну пам’ять. Я думав, що, можливо, існує інструмент / пакет Java, який дозволяє просто побудувати (за допомогою інтерфейсу коду) макет БД на тест, із змодельованими таблицями та рядками, з перевіркою SQL та з інтерфейсом коду для моніторингу його стану (а не за допомогою SQL ).

Ви знайомі з цим видом інструменту?


Редагувати: Дякую за відповіді! Хоча я просив інструмент, ви також надали мені декілька порад щодо проблеми :) Щоб перевірити ваші пропозиції, мені знадобиться деякий час, тож зараз я не можу сказати, чи ваші відповіді не задовольняли.

У будь-якому випадку, ось кращий погляд на те, що я шукаю - уявіть клас з іменем DBMonitor, що однією з його особливостей є знаходження кількості рядків у таблиці. Ось уявний код того, як я хотів би протестувати цю функцію за допомогою JUnit:

public class TestDBMonitor extends TestCase {

    @Override
    public void setUp() throws Exception {

       MockConnection connection = new MockConnection();

       this.tableName = "table1";
       MockTable table = new MockTable(tableName);

       String columnName = "column1";
       ColumnType columnType = ColumnType.NUMBER;
       int columnSize = 50;
       MockColumn column = new MockColumn(columnName, columnType, columnSize);
       table.addColumn(column);

       for (int i = 0; i < 20; i++) {
           HashMap<MockColumn, Object> fields = new HashMap<MockColumn, Object>();
           fields.put(column, i);
           table.addRow(fields);
       }

       this.connection = connection;
    }

    @Test
    public void testGatherStatistics() throws Exception {

       DBMonitor monitor = new DBMonitor(connection);
       monitor.gatherStatistics();
       assertEquals(((MockConnection) connection).getNumberOfRows(tableName),
                    monitor.getNumberOfRows(tableName));
    }

    String tableName;
    Connection connection;
}

Сподіваюсь, цей код достатньо зрозумілий, щоб зрозуміти мою ідею (вибачте за синтаксичні помилки, я вводив вручну без мого дорогого Eclipse: P).

До речі, я частково використовую ORM, і мої необроблені запити SQL досить прості і не повинні відрізнятися від однієї платформи до іншої.

Відповіді:


24

нова відповідь на старе запитання (але справа трохи зрушилася вперед):

Як імітувати БД для тестування (Java)?

ви не імітуєте це. ви знущаєтесь зі своїх репозиторіїв, і ви не тестуєте їх, або використовуєте той самий db у своїх тестах і тестуєте свої sqls. Всі бази даних в пам'яті не повністю сумісні, тому вони не дадуть вам повного покриття та надійності. і ніколи не намагайтеся знущатись / імітувати такі глибокі db-об'єкти, як з'єднання, набір результатів тощо, це взагалі не дає вам ніякої цінності і є кошмаром для розробки та підтримки

мати персональне тестування БД досить неможливо. Ви повинні використовувати "загальнодоступну" БД, доступну кожному

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

"Ці тести не є швидкими ..." - Тести DB, як правило, повільніші, ніж звичайні тести. Насправді не ідеально мати повільні тести.

так, db-тести повільніші, але вони не такі повільні. Я зробив кілька простих вимірювань, і типовий тест зайняв 5-50 мс. потрібен час - це запуск програми. Є безліч способів пришвидшити це:

  • Перші фреймворки DI (наприклад, весна) пропонують запуск лише деякої частини вашої програми. якщо ви пишете свою заявку з хорошим розділенням логіки db та не пов'язаної з db, тоді у вашому тесті ви можете запустити лише частину db
  • кожен дБ має безліч варіантів налаштування, що робить його менш міцним і набагато швидшим. це ідеально підходить для тестування. приклад postgres
  • Ви також можете помістити весь db в tmpfs

  • ще одна корисна стратегія - мати групи тестів і тримати db-тести вимкненими за замовчуванням (якщо вони дійсно уповільнюють вашу збірку). таким чином, якщо хтось насправді працює над db, йому потрібно передати додатковий прапор у рядку cmd або використовувати IDE (групи testng та власні селектори тесту ідеально підходять для цього)

Для кожного випадку слід робити певну кількість запитів на вставку / оновлення, що дратує і вимагає часу

Частина "займає час" обговорювалася вище. це дратує? Я бачив два шляхи:

  • підготуйте один набір даних для всіх тестів. тоді ви повинні підтримувати це і міркувати про це. зазвичай це відокремлено від коду. він має кілобайти або мегабайти. це велике бачити на одному екрані, розуміти і міркувати про. це вводить зв'язок між тестами. тому що, коли вам потрібно більше рядків для тесту A, ваш count(*)тест в B не вдається. вона зростає лише тому, що навіть коли ви видаляєте деякі тести, ви не знаєте, які рядки використовував лише цей один тест
  • кожне тестування готує свої дані. таким чином, кожен тест є повністю незалежним, читабельним і простим для міркувань. це дратує? imo, зовсім не! це дозволяє швидко писати нові тести та економить багато роботи в майбутньому

звідки ви знаєте, що в цій таблиці є 542 рядки? "- Одним з основних принципів тестування є можливість перевірити функціональність, яка відрізняється від функції тестованого коду.

ммм ... не дуже. головний принцип - перевірити, чи генерує ваше програмне забезпечення бажаний результат у відповідь на конкретний вхід. отже, якщо ви телефонуєте dao.insert542 рази, а потім dao.countповертаєте 542, це означає, що ваше програмне забезпечення працює, як зазначено. якщо ви хочете, ви можете викликати кеш / скидання кешу між ними. Звичайно, іноді ви хочете перевірити свою реалізацію замість контракту, а потім перевірити, чи змінив ваш дао стан баз даних. але ви завжди тестуєте sql A, використовуючи sql B (вставка проти вибору, послідовність next_val проти поверненого значення тощо). так, у вас завжди буде проблема "хто буде тестувати мої тести", і відповідь така: ніхто, тож будьте простими!

інші інструменти, які можуть вам допомогти:

  1. testcontainers допоможуть вам надати реальну базу даних.

  2. dbunit - допоможе вам очистити дані між тестами

    мінуси:

    • для створення та обслуговування схеми та даних потрібно багато роботи. особливо коли ваш проект знаходиться на стадії інтенсивного розвитку.
    • це ще один абстракційний шар, тому якщо раптом ви захочете скористатися якоюсь функцією db, яка не підтримується цим інструментом, може бути важко протестувати її
  3. тестування - має намір надати вам повний, готовий до використання та розширюваний життєвий цикл (розкриття: я творець).

    мінуси:

    • безкоштовно лише для невеликих проектів
    • дуже молодий проект
  4. пролітний або LiquiBase - DB інструменти міграції. вони допомагають легко створювати схеми та всі структури у вашому локальному базі даних для тестів.


6
Я повинен передати його вам, я не думав, що хтось перегляне це питання і потрудиться написати оновлену відповідь. Я запитав це 8 років тому, і з тих пір я набув досвіду, який змушує мене здебільшого погоджуватися з вашою відповіддю - особливо з частиною про "тестування функціональності з тим самим кодом".
Eyal Roth

40

Java поставляється з Java DB .

Тим не менш, я б радив не використовувати інший тип БД, ніж той, який ви використовуєте у виробництві, якщо не проходите рівень ORM. В іншому випадку ваш SQL може бути не таким крос-платформним, як ви думаєте.

Також перевірте DbUnit


10

Для цього я використовував Hypersonic . По суті, це файл JAR (чиста база даних Java у пам’яті), який ви можете запускати у власній JVM або у власній JVM, і поки він працює, у вас є база даних. Потім ви зупините це, і ваша база даних зникне. До цього часу я використовував її як суто вбудовану базу даних. Запустити і зупинити за допомогою Ant дуже просто під час запуску модульних тестів.


10

Існує безліч точок зору щодо тестування точок інтеграції, таких як підключення до бази даних через SQL. Мій особистий набір правил, який добре для мене працював, такий:

1) Відокремте логіку та функції доступу до бази даних від загальної бізнес-логіки та сховайте її за інтерфейсом. Причина: Для того, щоб перевірити переважну більшість логіки в системі, найкраще використовувати манекен / заглушку замість фактичної бази даних, як її простішу. Причина 2: Це значно швидше

2) Розгляньте тести для бази даних як тести інтеграції, які відокремлені від основної частини модульних тестів і потребують запуску в базі даних налаштування Причина: Швидкість та якість тестів

3) Кожному розробнику знадобиться своя окрема база даних. Вони потребуватимуть автоматизованого способу оновлення його структури на основі змін від їхніх товаришів по команді та введення даних. Див. Пункти 4 та 5.

4) Використовуйте такий інструмент, як http://www.liquibase.org для управління оновленнями у вашій структурі баз даних. Причина: надає вам спритність у можливості змінити існуючу структуру та рухатися вперед у версіях

5) Використовуйте такий інструмент, як http://dbunit.sourceforge.net/, для управління даними. Налаштуйте файли сценаріїв (xml або XLS) для конкретних тестових випадків та базових даних та чітко визначте, що потрібно для будь-якого тестового випадку. Причина: набагато краще, ніж вручну вставляти та видаляти дані Причина 2: Тестерам легше зрозуміти, як налаштувати сценарії Причина 3: Швидше виконати це

6) Вам потрібні функціональні тести, які також мають DBUnit, як дані сценарію, але це набагато більші набори даних і виконують всю систему. Це завершує крок поєднання знань про те, що: а) запущені модульні тести і, отже, логіка обгрунтована; б) тести інтеграції до запуску бази даних і SQL правильні, в результаті чого "і система в цілому працює разом як вершина до нижній стек "

Ця комбінація послужила мені на сьогоднішній день для досягнення високої якості тестування та продукту, а також для підтримки швидкості розробки модульного тесту та спритності до змін.


6

"Просто придбайте собі тестову БД, наскільки це може бути важко?" - Ну, на моєму робочому місці мати персональне тестування БД досить неможливо. Ви повинні використовувати "загальнодоступну" БД, доступну для всіх.

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

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


6

Якщо ви використовуєте Oracle на роботі, ви можете скористатися функцією "Точка відновлення в базі даних Flashback", щоб повернути базу даних до часу перед тестами. Це видалить будь-які зміни, які ви особисто внесли в БД.

Подивитися:

https://docs.oracle.com/cd/E11882_01/backup.112/e10642/flashdb.htm#BRADV71000

Якщо вам потрібна тестова база даних для використання з виробництвом / роботою Oracle, тоді шукайте XE, експрес-базу даних від Oracle. Це безкоштовно для особистого користування, при обмеженні бази даних менше 2 Гб.


3

Нещодавно ми перейшли на JavaDB або Derby для реалізації цього. Derby 10.5.1.1 тепер реалізує представлення в пам'яті, тому працює дуже швидко, не потрібно переходити на диск: Derby In Memory Primer

Ми розробляємо наш додаток для роботи на Oracle, PostgreSQL та Derby, щоб ми не зайшли занадто далеко на жодній платформі, перш ніж з'ясувати, що одна база даних підтримує функцію, яку інші не підтримують.


1

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


1

Спробуйте використати дербі . Це легко і портативно. За допомогою Hibernate ваш додаток стає гнучким. Тест на дербі, постановка на все, що вам подобається і довіряєте.


1

Зараз ми створюємо тестове середовище для роботи з базою даних. Ми вважаємо, що ми повинні використовувати реальну систему управління базами даних із змодельованими даними . Однією з проблем модельованої СУБД є те, що SQL ніколи не був повністю гельованим як стандарт, тому штучне середовище тестування мало б сумлінно підтримувати діалект нашої виробничої бази даних. Інша проблема полягає в тому, що ми широко використовуємо обмеження значень стовпців, обмеження зовнішнього ключа та унікальні обмеження, і оскільки штучний інструмент, ймовірно, не реалізував би їх, наші модульні тести могли б пройти, але наші системні тести зазнали б невдачі, коли вони вперше потрапили в реальну обмеження. Якщо тести займають занадто багато часу, це вказує на помилку реалізації, і ми налаштуємо наші запити (зазвичай набори тестових даних є незначними порівняно з виробничими).

Ми встановили справжню СУБД на кожній машині розробника та на нашому сервері постійної інтеграції та тестування (ми використовуємо Hudson). Я не знаю, якими є обмеження вашої робочої політики, але встановити та використовувати PostgreSQL, MySQL та Oracle XE досить просто. Усі вони є безкоштовними для розробки (навіть Oracle XE), тому немає розумних підстав забороняти їх використання.

Ключове питання полягає в тому, як ви гарантуєте, що ваші тести завжди починаються з бази даних у стабільному стані? Якщо всі тести були лише для читання, не біда. Якщо б ви могли спроектувати мутаційні тести, щоб завжди запускати транзакції, які ніколи не фіксуються, не біда. Але, як правило, вам потрібно турбуватися про те, щоб змінити зміни на стор. Для цього ви можете експортувати початковий стан у файл, а потім імпортувати його назад після тесту (це роблять команди exp та imp оболонки Oracle). Або ви можете використовувати контрольний пункт / відкат. Але більш елегантний спосіб - використовувати такий інструмент, як dbunit , який добре працює для нас.

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



1

jOOQ - це інструмент, який, крім пропонування абстракції SQL, також має вбудовані невеликі інструменти, такі як SPI, який дозволяє знущатися над цілим JDBC. Це може працювати двома способами, як зафіксовано в цьому дописі в блозі :

Впроваджуючи MockDataProviderSPI:

// context contains the SQL string and bind variables, etc.
MockDataProvider provider = context -> {

    // This defines the update counts, result sets, etc.
    // depending on the context above.
    return new MockResult[] { ... }
};

У наведеній вище реалізації ви можете програмно перехопити кожен оператор SQL і повернути результат для нього, навіть динамічно, "аналізуючи" рядок SQL для вилучення деяких предикатів / інформації таблиці тощо.

Використовуючи простіший (але менш потужний) MockFileDatabase

... який має такий формат, як наведений нижче (набір пар тверджень / результатів):

select first_name, last_name from actor;
> first_name last_name
> ---------- ---------
> GINA       DEGENERES
> WALTER     TORN     
> MARY       KEITEL   
@ rows: 3

Потім наведений файл можна прочитати та використати наступним чином:

import static java.lang.System.out;
import java.sql.*;
import org.jooq.tools.jdbc.*;

public class Mocking {
    public static void main(String[] args) throws Exception {
        MockDataProvider db = new MockFileDatabase(
            Mocking.class.getResourceAsStream("/mocking.txt");

        try (Connection c = new MockConnection(db));
            Statement s = c.createStatement()) {

            out.println("Actors:");
            out.println("-------");
            try (ResultSet rs = s.executeQuery(
                "select first_name, last_name from actor")) {
                while (rs.next())
                    out.println(rs.getString(1) 
                        + " " + rs.getString(2));
            }
        }
    }
}

Зверніть увагу, як ми використовуємо API JDBC безпосередньо, фактично не підключаючись до жодної бази даних.

Зауважте, я працюю у постачальника jOOQ, тому ця відповідь необ’єктивна.

Остерігайтеся, у певний момент ви впроваджуєте цілу базу даних

Вищезазначене працює для простих випадків. Але пам’ятайте, що врешті-решт ви впровадите цілу базу даних. Ти хочеш:

  1. Перевірте синтаксис SQL.

Добре, знущаючись над базою даних, як показано вище, ви можете "перевірити" синтаксис, оскільки кожен такий синтаксис, який ви не передбачали в точній версії, як зазначено вище, буде відхилений будь-яким таким глумливим підходом.

Ви можете реалізувати синтаксичний аналізатор, який аналізує SQL ( або, знову ж таки, використовувати jOOQ ), а потім перетворити оператор SQL на те, що ви можете легше розпізнати і отримати результат. Але зрештою, це просто означає впровадження цілої бази даних.

  1. Що ще важливіше, переконайтесь, що дані обрані / оновлені / вставлені правильно, відповідно до певної ситуації.

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

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

Знущання доставить вас лише так далеко

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


1

Я думаю, що мій фреймворк Acolyte можна використовувати для таких макетів БД: https://github.com/cchantep/acolyte .

Це дозволяє запускати існуючу Java (для тестування) із підключеннями, які ви обробляєте для обробки запитів / оновлення: повертаючи відповідні набори результатів, кількість оновлень або попередження відповідно до випадків виконання.


0

Ну для початку, чи використовуєте ви будь-який рівень ORM для доступу до БД?
Якщо ні: тоді те, про що ви думаєте, не принесе користі. Яка користь від тестування, коли ви не впевнені, що SQL, який ви запускаєте, працюватиме з вашою БД у виробництві, як і в тестових випадках, коли ви використовуєте щось інше.
Якщо так: Тоді ви можете поглянути на різні варіанти.

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