it-roy-ru.com

Как смоделировать БД для тестирования (Java)?

Я программирую на Java, и мои приложения активно используют БД. Следовательно, для меня важно иметь возможность легко проверить использование моей БД.
О каких тестах БД все идет? Для меня они должны предоставить два простых требования: 

  1. Проверьте синтаксис SQL.
  2. Что еще более важно, проверьте, что данные выбраны/обновлены/вставлены правильно, в соответствии с данной ситуацией. 

Что ж, похоже, все, что мне нужно, это БД.
Но на самом деле, я предпочитаю нет, так как есть немного трудностей с использованием БД для теста: 

  • "Просто возьмите себе тестовую базу данных, насколько это может быть сложно?" - Ну, на моем рабочем месте, иметь личное тестирование БД довольно невозможно. Вы должны использовать «публичную» БД, которая доступна для всех.
  • «Эти тесты, конечно, не быстрые ...» - тесты БД, как правило, медленнее, чем обычные тесты. Это действительно не идеально, чтобы иметь медленные тесты.
  • "Эта программа должна обрабатывать любые случаи!" - Это становится несколько раздражающим и даже невозможным, чтобы попытаться смоделировать каждый случай в БД. Для каждого случая следует выполнить определенное количество запросов на вставку/обновление, что раздражает и занимает много времени.
  • «Подождите секунду, откуда вы знаете, что в этой таблице 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-запросы довольно просты и не должны отличаться от одной платформы к другой. 

59
Eyal Roth

новый ответ на старый вопрос (но дела немного продвинулись):

Как смоделировать БД для тестирования (Java)?

вы не симулируете это. вы издеваетесь над своими репозиториями и не тестируете их, или вы используете один и тот же БД в своих тестах и ​​тестируете свои sqls. Все базы данных в памяти не полностью совместимы, поэтому они не дадут вам полного охвата и надежности. и никогда не пытайтесь смоделировать/смоделировать объекты глубокой базы данных, такие как соединение, набор результатов и т. д., это вообще не дает никакой ценности и является кошмаром для разработки и поддержки

иметь личное тестирование БД довольно невозможно. Вы должны использовать «публичную» БД, которая доступна каждому

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

«Эти тесты, конечно, не быстрые ...» - тесты БД, как правило, медленнее, чем обычные тесты. Это действительно не идеально, чтобы иметь медленные тесты.

да, тесты БД медленнее, но не такие медленные. Я сделал несколько простых измерений и типичный тест занял 5-50 мс. Что требует времени, так это запуск приложения. Есть много способов ускорить это:

  • первые DI-фреймворки (например, Spring) предлагают способ запуска только некоторой части вашего приложения. если вы пишете свое приложение с хорошим разделением логики, связанной с БД и без БД, то в своем тесте вы можете запустить только часть БД
  • каждый БД имеет множество опций настройки, что делает его менее долговечным и намного более быстрым. это идеально подходит для тестирования. пример postgres
  • вы также можете поместить всю базу данных в tmpfs

  • еще одна полезная стратегия - иметь группы тестов и по умолчанию отключать тесты БД (если они действительно замедляют вашу сборку). таким образом, если кто-то действительно работает с БД, ему нужно передать дополнительный флаг в строке cmd или использовать IDE (группы testng и пользовательские селекторы тестов идеально подходят для этого)

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

часть «занимает время» обсуждалась выше. это раздражает? Я видел два пути:

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

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

хм ... не совсем. Основной принцип - проверить, генерирует ли ваше программное обеспечение желаемый результат в ответ на конкретный вход. поэтому, если вы вызываете dao.insert 542 раза, а затем ваш dao.count возвращает 542, это означает, что ваше программное обеспечение работает, как указано. если вы хотите, вы можете вызвать commit/drop кеш между ними. Конечно, иногда вы хотите протестировать свою реализацию вместо контракта, а затем проверить, изменил ли ваш дао состояние базы данных. но вы всегда тестируете sql A, используя sql B (вставка против выбора, последовательность next_val против возвращенного значения и т. д.). да, у вас всегда будет проблема «кто будет тестировать мои тесты», и ответ таков: никто, так что будьте проще!

другие инструменты, которые могут вам помочь:

  1. testcontainers поможет вам предоставить real db.

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

    минусы: 

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

    минусы: 

    • бесплатно только для небольших проектов
    • очень молодой проект
  4. flyway или liquibase - db инструменты для миграции. они помогают вам легко создавать схемы и все структуры в вашей локальной базе данных для тестов.

12
piotrek

Java поставляется с Java DB .

Тем не менее, я бы посоветовал не использовать другой тип БД, чем тот, который вы используете в производстве, если вы не пройдете уровень ORM. В противном случае ваш SQL может быть не таким кроссплатформенным, как вы думаете.

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

40
ykaganovich

Я использовал Hypersonic для этой цели. По сути, это JAR-файл (чистая база данных Java в памяти), который вы можете запустить в своей собственной JVM или в собственной JVM, и пока он работает, у вас есть база данных. Тогда вы останавливаете это, и ваша база данных исчезает. До сих пор я использовал его как базу данных исключительно в памяти. При запуске модульных тестов запускать и останавливать через Ant очень просто.

10
Eddie

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

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

2) Относитесь к тестам для базы данных как к интеграционным тестам, которые отделены от основной части модульных тестов и должны выполняться в базе данных настройки Причина: скорость и качество тестов

3) Каждому разработчику понадобится своя отдельная база данных. Им понадобится автоматизированный способ обновления его структуры на основе изменений от их товарищей по команде и представления данных. Смотрите пункты 4 и 5.

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

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

6) Вам нужны функциональные тесты, которые также имеют DBUnit, как данные сценария, но это гораздо большие наборы данных и выполняют всю систему. Это завершает этап объединения знаний о том, что A) модульные тесты выполняются, и, следовательно, логика надежна B) что интеграционные тесты для базы данных выполняются, и SQL корректен Приводя к "и системе в целом работает вместе как стопка сверху вниз

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

10
Paul Keeble

"Просто возьмите себе тестовую базу данных, насколько это может быть сложно?" - Ну, на моем рабочем месте, иметь личное тестирование БД довольно невозможно. Вы должны использовать «публичную» БД, которая доступна для всех.

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

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

6
banjollity

Если вы используете Oracle на работе, вы можете использовать точку восстановления в базе данных Flashback, чтобы база данных возвращалась ко времени, предшествующему вашим тестам. Это удалит любые изменения, которые вы лично внесли в БД.

Увидеть:

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

Если вам нужна тестовая база данных для использования с производством/работой Oracle, тогда ищите базу данных XE, экспресс-издание от Oracle. Это бесплатно для личного использования, с размером базы данных менее 2 ГБ.

4
Martlark

Мы недавно переключились на JavaDB или Derby , чтобы реализовать это. Derby 10.5.1.1 теперь реализует представление в памяти, поэтому он работает очень быстро, ему не нужно переходить на диск: Derby In Memory Primer

Мы разрабатываем наше приложение для работы с Oracle, PostgreSQL и Derby, поэтому мы не заходим слишком далеко на любой платформе, прежде чем узнаем, что одна база данных поддерживает функцию, которую другие не поддерживают.

3
Blair Zajac

Я думаю, что мой каркас Acolyte может быть использован для такого макета БД: https://github.com/cchantep/acolyte .

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

1
cchantep

Мы создаем среду тестирования базы данных на работе прямо сейчас. Мы считаем, что должны использовать real систему управления базами данных с моделируемыми данными. Одна из проблем с имитируемой СУБД заключается в том, что SQL никогда не был полностью замаскирован в качестве стандарта, поэтому среда искусственного тестирования должна была бы верно поддерживать диалект нашей производственной базы данных. Другая проблема заключается в том, что мы широко используем ограничения значений столбца, ограничения внешнего ключа и ограничения уникальности, и, поскольку искусственный инструмент, вероятно, не реализует их, наши модульные тесты могут пройти, но наши системные тесты не пройдут, когда они впервые достигнут реального ограничения. Если тесты занимают слишком много времени, это указывает на ошибку реализации, и мы настраиваем наши запросы (обычно наборы тестовых данных незначительны по сравнению с производственными).

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

Ключевой вопрос - как вы гарантируете, что ваши тесты всегда начинаются с базы данных в согласованном состоянии? Если бы тесты были только для чтения, нет проблем. Если бы вы могли создать мутационные тесты, которые всегда выполняются в транзакциях, которые никогда не фиксируются, не проблема. Но обычно вам нужно беспокоиться об отмене обновлений. Для этого вы можете экспортировать исходное состояние в файл, а затем импортировать его обратно после тестирования (для этого используются команды Oracle exp и imp Shell). Или вы можете использовать контрольно-пропускной пункт/откат. Но более элегантный способ - использовать такой инструмент, как dbunit , который хорошо работает для нас.

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

1
Jim Ferrans

Попробуйте использовать дерби . Это легко и портативно. С Hibernate ваше приложение становится гибким. Тест на дерби, производство на что угодно и кому доверяешь.

1
Artic

Я согласен с banjollity. Настройка изолированных сред разработки и тестирования должна быть приоритетной задачей. Каждая система баз данных, которую я использовал, имеет либо открытый исходный код, либо имеет бесплатную версию для разработчиков, которую можно установить на локальную рабочую станцию. Это позволяет разрабатывать на том же диалекте базы данных, что и производственный, дает вам полный административный доступ к базам данных разработки и быстрее, чем при использовании удаленного сервера. 

1
Nat

Вы можете HSQLDB для тестирования в памяти БД. Запуск базы данных в памяти и запуск тестов на ней довольно прост.
http://hsqldb.org/

1
Pratik Singhal

jOOQ - это инструмент, который помимо абстракции SQL также имеет встроенные небольшие инструменты, такие как SPI, который позволяет имитировать весь JDBC. Это может работать двумя способами, как описано в этом сообщении в блоге :

Путем реализации MockDataProvider SPI:

// 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));
            }
        }
    }
}

Обратите внимание, как мы используем JDBC API напрямую, без фактического подключения к какой-либо базе данных.

Заметьте, я работаю на продавца jOOQ, поэтому этот ответ предвзят.

Остерегайтесь, в какой-то момент вы реализуете всю базу данных

Вышесказанное работает для простых случаев. Но будьте осторожны, что, в конце концов, вы будете реализовывать всю базу данных. Ты хочешь:

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

ОК, смоделировав базу данных, как показано выше, вы можете «проверить» синтаксис, потому что любой синтаксис, который вы не предусмотрели в версии точной, как указано выше, будет отвергнут любым таким подходом.

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

  1. Что еще более важно, проверьте, что данные выбраны/обновлены/вставлены правильно, в соответствии с данной ситуацией.

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

Как вы убедитесь, что это происходит при "издевательстве" над базой данных? Вам нужен конечный автомат, который запоминает состояние каждой «ложной» таблицы. Другими словами, вы создадите базу данных.

Насмешка приведет вас так далеко

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

0
Lukas Eder

Ну, для начала, используете ли вы какой-либо уровень ORM для доступа к БД?
Если нет: то, что вы думаете, будет бесполезно. Какой смысл использовать тестирование, когда вы не уверены, что SQL, который вы запускаете, будет работать с вашей БД в производственной среде, как в тестовых примерах, когда вы используете что-то другое ..
Если да: то вы можете посмотреть на различные варианты, указанные.

0
Khangharoth