Ниже представлен опыт нагрузочного тестирования базы данных с использованием JUnit и ассоциированных с ним DBUnit и ContiPerf.
ContiPerf — утилита, которая позволяет использовать JUnit4 для нагрузочных тестов. Проста в использовании, легко и разнообразно настраивается. Использует Java аннотации для задания настроек теста и требований выполнения, создает подробный отчет в виде html файла с графиком распределения времени выполнения. Требует использование Java не ниже 5 версии и JUnit не ниже версии 4.7.
DBUnit — расширение для JUnit, упрощающее тестирование программ, работающих с бд. Впрочем, вполне популярен и в представлении не нуждается, поэтому ограничусь ссылками: знакомство для начинающих, упоминания на хабре.
Для начала создадим файл с параметрами подключения к базе данных. Здесь используются Spring JavaConfig и для нагрузочного тестирования необходим пул соединений, в качестве которого выбран Tomcat JDBC Connection Pool. О последнем и способах его использования можно прочитать здесь и здесь. Пул выбирался по принципу первый с примерами в списке google запроса «connection pool».
Далее настроим DBUnit. В общей сложности получится два конфигурационных класса. Первый — JavaConf — служит для описания настроек соединения при желании и к нескольким базам данных и далее позволяет легко подключать нужную бд к тесту. Ниже будет создан класс, при наследовании от которого появится возможность использовать уже настроенный функционал DBUnit, и не нужно будет настраивать его каждый раз. Эти классы можно использовать и при другом виде, не только при нагрузочном тестировании.
Здесь используется небольшой трюк. Дело в том, что JUnit позволяет только один раз использовать аннотацию @RunWith, которая будет использована позже, что в свою очередь не позволяет воспользоваться классическим @RunWith(SpringJUnit4ClassRunner.class). Поэтому нужно вручную создавать экземпляр контекста и подтягивать его вот этим участком кода(с):
Чтобы понять вид используемой здесь магии следует обратиться к документации соответствующего класса.
Наконец, переходим непосредственно к тестовому классу:
В catch, как идея к дополнениям, можно добавить счетчик ошибок и выводить их количество в методе с аннотацией after.
Больше настроек для ContiPerf можно найти на официальной странице: можно, например, в аннотации @PerfTest задавать не длительность теста, а количество его выполнения с помощью параметра invocations.
Краткий отчет для каждого тестового метода пишется в консоли после выполнения, более детальный отчет (пример приведен ниже) с подробной статистикой находится в файле target/contiperf-report/index.html.

Для решения данной задачи вначале была попытка использовать инструмент для тестирования SoapUI и JDBC запросы, однако возникла проблема — частое, методичное выпадение ошибки: «java.sql.SQLException: Listener refused the connection with the following error:ORA-12519, TNS:no appropriate service handler found» — не нашедшая решения. Позже нашлось другое решение с использованием другого инструмента — JMeter, но слишком поздно…
P.S.: Комментарии, нарекания, дополнения, возражения, напутствия приветствуются и очень полезны.
ContiPerf
ContiPerf — утилита, которая позволяет использовать JUnit4 для нагрузочных тестов. Проста в использовании, легко и разнообразно настраивается. Использует Java аннотации для задания настроек теста и требований выполнения, создает подробный отчет в виде html файла с графиком распределения времени выполнения. Требует использование Java не ниже 5 версии и JUnit не ниже версии 4.7.
DBUnit
DBUnit — расширение для JUnit, упрощающее тестирование программ, работающих с бд. Впрочем, вполне популярен и в представлении не нуждается, поэтому ограничусь ссылками: знакомство для начинающих, упоминания на хабре.
Тест
Для начала создадим файл с параметрами подключения к базе данных. Здесь используются Spring JavaConfig и для нагрузочного тестирования необходим пул соединений, в качестве которого выбран Tomcat JDBC Connection Pool. О последнем и способах его использования можно прочитать здесь и здесь. Пул выбирался по принципу первый с примерами в списке google запроса «connection pool».
@Configuration @Profile("db1") public class DBConfig { @Bean public javax.sql.DataSource.DataSource dataSource(){ org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource(); ds.setDriverClassName("dbDriver"); ds.setUrl("dbUrl"); ds.setUsername("dbUser"); ds.setPassword(""); ds.setInitialSize(5); ds.setMaxActive(10); ds.setMaxIdle(5); ds.setMinIdle(2); return ds; } }
Далее настроим DBUnit. В общей сложности получится два конфигурационных класса. Первый — JavaConf — служит для описания настроек соединения при желании и к нескольким базам данных и далее позволяет легко подключать нужную бд к тесту. Ниже будет создан класс, при наследовании от которого появится возможность использовать уже настроенный функционал DBUnit, и не нужно будет настраивать его каждый раз. Эти классы можно использовать и при другом виде, не только при нагрузочном тестировании.
@ContextConfiguration(classes = DBConfig.class) @ActiveProfiles("db1") public class DBUnitConfig extends DBTestCase{ @Autowired javax.sql.DataSource dataSource; protected IDatabaseTester tester; protected IDataSet beforeData; private TestContextManager testContextManager; @Before public void setUp() throws Exception { this.testContextManager = new TestContextManager(getClass()); this.testContextManager.prepareTestInstance(this); tester = new DataSourceDatabaseTester(dataSource); } @Override protected IDataSet getDataSet() throws Exception { return beforeData; } @Override protected DatabaseOperation getTearDownOperation() throws Exception { return DatabaseOperation.NONE; } }
Здесь используется небольшой трюк. Дело в том, что JUnit позволяет только один раз использовать аннотацию @RunWith, которая будет использована позже, что в свою очередь не позволяет воспользоваться классическим @RunWith(SpringJUnit4ClassRunner.class). Поэтому нужно вручную создавать экземпляр контекста и подтягивать его вот этим участком кода(с):
// танец с бубном this.testContextManager = new TestContextManager(getClass()); this.testContextManager.prepareTestInstance(this);
Чтобы понять вид используемой здесь магии следует обратиться к документации соответствующего класса.
Наконец, переходим непосредственно к тестовому классу:
// как и обещано RunWith использован, здесь для параллельного выполнения тестовых методов @RunWith(ParallelRunner.class) // настройки нагрузочного теста: продолжительность в мс, количество потоков, // время ожидания между тестами в мс (случайно выбирается из указанного интервала) @PerfTest(duration = 10000, threads = 50, timer = RandomTimer.class, timerParams = { 5, 15 }) // требования к тесту: максимальное и среднее время выполнения, не является обязательным полем @Required(max = 500, average = 100) public class PerfTest extends DBUnitConfig { // активация ContiPerf @Rule public ContiPerfRule i = new ContiPerfRule(); // метод для инициализации данных необходимых для теста @Before public void setUp() throws Exception { super.setUp(); // тривиальный пример xml файла набора данных: "<dataset></dataset>" beforeData = new FlatXmlDataSetBuilder().build( Thread.currentThread().getContextClassLoader() .getResourceAsStream("initData.xml")); tester.setDataSet(beforeData); tester.onSetup(); } // тестовый метод @Test public void test1() { Connection sqlConnection = null; CallableStatement statement = null; ResultSet set = null; try { sqlConnection = tester.getConnection().getConnection(); sqlConnection.setAutoCommit(true); statement = sqlConnection.prepareCall("select smth from tbl where param=?"); statement.setString(1, "prm"); set = statement.executeQuery(); set.next(); int smth1 = set.getInt(1); statement.close(); statement = sqlConnection.prepareCall("{ ? = call pkg.function(?) }"); statement.registerOutParameter(1, (Types.INTEGER)); statement.setString(2, "prm"); statement.execute(); int smth2 = statement.getInt(1); statement.close(); } catch(Exception ex){ System.out.print(ex.getMessage()); } finally { try { set.close(); statement.close(); sqlConnection.close();} catch (Exception e){ System.out.print(e.getMessage());} } Assert.assertEquals(smth1, smth2); } }
В catch, как идея к дополнениям, можно добавить счетчик ошибок и выводить их количество в методе с аннотацией after.
Больше настроек для ContiPerf можно найти на официальной странице: можно, например, в аннотации @PerfTest задавать не длительность теста, а количество его выполнения с помощью параметра invocations.
Краткий отчет для каждого тестового метода пишется в консоли после выполнения, более детальный отчет (пример приведен ниже) с подробной статистикой находится в файле target/contiperf-report/index.html.

Для решения данной задачи вначале была попытка использовать инструмент для тестирования SoapUI и JDBC запросы, однако возникла проблема — частое, методичное выпадение ошибки: «java.sql.SQLException: Listener refused the connection with the following error:ORA-12519, TNS:no appropriate service handler found» — не нашедшая решения. Позже нашлось другое решение с использованием другого инструмента — JMeter, но слишком поздно…
P.S.: Комментарии, нарекания, дополнения, возражения, напутствия приветствуются и очень полезны.
