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

Как создать единичные тесты с неинформационной базой данных, такими как MySQL в платформе Play, с сбросом в известное состояние?

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

Тестирование встроенной базы данных H2

Документация по платформе приложений предлагает запустить модульные тесты в базе данных H2 в памяти, даже если основная база данных, используемая для разработки и производства, использует другое программное обеспечение (то есть MySQL):

app = Helpers.fakeApplication(Helpers.inMemoryDatabase());

Мое приложение не использует сложные функции RDBMS, такие как хранимые процедуры, и большинство случаев доступа к базе данных - это вызовы ebean, поэтому они должны быть совместимы как с MySQL, так и с H2.

Однако в операторах создания таблиц в эволюциях используются специфичные для MySQL функции, такие как указание ENGINE = InnoDB, DEFAULT CHARACTER SET = utf8 и т.д. Я боюсь, что если я удалю эти проприетарные части CREATE TABLE, MySQL будет использовать некоторые настройки по умолчанию, которые Я не могу контролировать и зависит от версии, поэтому для тестирования и разработки приложения основная конфигурация MySQL должна быть изменена.

Кто-нибудь использовал этот подход (делая эволюции совместимыми с MySQL и H2)?

Другие идеи, как это можно обработать:

  • Отдельные эволюции для MySQL и H2 (не очень хорошая идея)
  • Какой-то способ заставить H2 игнорировать дополнительный материал MySQL в CREATE TABLE (режим совместимости MySQL не работает, он все еще жалуется даже на default character set). Я не знаю, как.

Тестирование на том же драйвере базы данных, что и основная база данных

Единственное преимущество базы данных с внутренней памятью H2 в том, что она быстрая, и тестирование на том же драйвере базы данных, что и dev/production database, может быть лучше, потому что оно ближе к реальной среде.

Как это можно сделать прямо в платформе Play?

Пробовал:

Map<String, String> settings = new HashMap<String, String>();
settings.put("db.default.url", "jdbc:mysql://localhost/sometestdatabase");
settings.put("db.default.jndiName", "DefaultDS");
app = Helpers.fakeApplication(settings);

Похоже, что эволюция работает здесь, но как лучше всего очистить базу данных перед каждым тестом? Создавая собственный код, который обрезает каждую таблицу? Если это приведет к отбрасыванию таблиц, будут ли эволюции запускаться снова перед следующим тестом или они будут применены один раз за команду play test? Или один раз за вызов Helpers.fakeApplication()?

Каковы лучшие практики здесь? Слышал о dbunit, можно ли интегрировать его без особых проблем и причуд?

4b9b3361

Ответ 1

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

Что касается необходимости очистки базы данных между каждым тестом, вы можете использовать Ebean DdlGenerator для создания сценариев для создания чистой базы данных и аннотации JUnit @Before для автоматического выполнения этих сценариев перед каждым тестом.

Использование DdlGenerator может быть выполнено следующим образом:

    EbeanServer server = Ebean.getServer(serverName);
    ServerConfig config = new ServerConfig();
    DdlGenerator ddl = new DdlGenerator((SpiEbeanServer) server, new MySqlPlatform(), config);

Этот код можно поместить в базовый класс, который вы могли бы наследовать ваши тесты (или внутри пользовательского Runner, который вы можете использовать с аннотацией @RunWith).

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

Некоторые ссылки, которые могут быть полезны:

Ответ 2

Я использовал тот же движок базы данных, что и основная база данных, и dbunit для очистки перед каждым тестом.

public class SomeTest {
    // ...

    @Before
    public void startApp() throws Exception {
        // Set up connection to test database, different from main database. Config better should be used instead of hard-coding.
        Map<String, String> settings = new HashMap<String, String>();
        settings.put("db.default.url", "jdbc:mysql://localhost/somedatabase?characterEncoding=UTF-8&useOldAliasMetadataBehavior=true");
        settings.put("db.default.user", "root");
        settings.put("db.default.password", "root");
        settings.put("db.default.jndiName", "DefaultDS"); // make connection available to dbunit through JNDI
        app = Helpers.fakeApplication(settings);
        Helpers.start(app);

        databaseTester = new JndiDatabaseTester("DefaultDS");

        IDataSet initialDataSet = new FlatXmlDataSetBuilder().build(play.Play.application()
                .resourceAsStream("/resources/dataset.xml"));
        databaseTester.setDataSet(initialDataSet);
        databaseTester.onSetup();
    }

    @After
    public void stopApp() throws Exception {
        databaseTester.onTearDown();
        Helpers.stop(app);
    }
}

My dataset.xml просто содержит имена таблиц, чтобы сообщить dbunit о том, чтобы очистить эти таблицы перед каждым тестом. Он также может содержать светильники.

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
  <name_of_my_first_table />
  <name_of_my_second_table />
</dataset>

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

Обидно использовать dbunit, если вам нужно только очистить таблицы, вы можете очистить их, выдав запрос напрямую или используя ebean DdlGenerator. Но я также использую dbunit для сравнения данных.

Я не использую Helpers.running, потому что он принимает Runnable и Runnable реализации не могут генерировать исключения - очень неудобно для тестов. Но если вы посмотрите код для running(), он просто вызывает Helpers.start() и Helpers.stop(), поэтому я вызываю эти методы непосредственно в @Before и @After.

Решено не использовать H2 для запуска тестов: да, он работает быстрее, но между ним и MySQL существует слишком большая разница.

Ответ 4

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

    @Test
public void testDataBase() {
    final HashMap<String,String> postgres = new HashMap<String, String>();
    postgres.put("db.default.driver","org.postgresql.Driver");
    postgres.put("db.default.url","jdbc:postgresql://localhost/myDataBase");
    postgres.put("db.default.user", "postgres");
    postgres.put("db.default.password", "password");

    running(fakeApplication(postgres), new Runnable() {

        @Override
        public void run() {

            //Insert Assertions Here
        }
    });
}

Ответ 5

Вы также можете использовать DB mock, если цель состоит в том, чтобы проверить и ваши сопоставления и функции Slick | JPA | Anorm на основе.

Когда он подходит, у него есть преимущество, чтобы быть более совместимым с модульным тестированием, чем тестовая БД, и более простым в управлении (не устанавливать/очищать задачи, а не синхронизировать тесты, чтобы избежать доступа к тем же тестовым таблицам).

Вы можете взглянуть на Acolyte (acolyte.eu.org), который используется в спецификациях самого Анорма (например, https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/SqlResultSpec.scala).