📜 ⬆️ ⬇️

Load testing database. ContiPerf + DBUnit

Below is the experience of load testing a database using JUnit and its associated DBUnit and ContiPerf.

Contiperf

ContiPerf is a utility that allows you to use JUnit4 for load tests. Easy to use, easy and versatile customizable. It uses Java annotations to specify test settings and execution requirements, creates a detailed report in the form of an html file with a schedule for the distribution of the execution time. Requires the use of Java version 5 or higher and JUnit version 4.7 or higher.

DBUnit

DBUnit is an extension for JUnit that simplifies testing programs that work with DB. However, it is quite popular and needs no introduction, so I will limit myself to references: acquaintance for beginners , references to Habré .
')
Test

First, create a file with database connection settings. Here Spring JavaConfig is used, and for load testing, a connection pool is needed, for which the Tomcat JDBC Connection Pool is selected. You can read about the latter and how to use it here and here . The pool was selected according to the principle of the first with examples in the google list of the connection connection request.
@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; } } 

Next, configure DBUnit. In total, there are two configuration classes. The first, JavaConf, is used to describe connection settings, if desired, to several databases and further allows you to easily connect the necessary database to the test. A class will be created below, with the inheritance from which it will be possible to use the already configured DBUnit functionality, and will not need to be configured each time. These classes can be used with a different form, not only with load testing.
 @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; } } 

It uses a little trick. The fact is that JUnit allows you to use the @RunWith annotation only once, which will be used later, which in turn does not allow you to use the classic @RunWith (SpringJUnit4ClassRunner.class). Therefore, you need to manually create an instance of the context and pull it up with this part of the code (s) :
 //    this.testContextManager = new TestContextManager(getClass()); this.testContextManager.prepareTestInstance(this); 

To understand the type of magic used here, refer to the documentation for the corresponding class.

Finally, go directly to the test class:
 //    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); } } 

In catch, as an idea for add-ons, you can add an error counter and display their number in the method with an annotation after.
More settings for ContiPerf can be found on the official page : for example, in the @PerfTest annotation, you can set the test duration, not the test duration, using the invocations parameter.
A brief report for each test method is written to the console after execution, a more detailed report (an example is shown below) with detailed statistics can be found in the file target / contiperf-report / index.html.
image
To solve this problem, at first there was an attempt to use the tool for testing SoapUI and JDBC requests, however, a problem arose - a frequent, methodical error occurred: "java.sql.SQLException: Listener: ORA-12519, TNS: no appropriate service handler found ”- not finding a solution. Later another solution was found using another tool - JMeter, but too late ...

PS: Comments, complaints, additions, objections, parting words are welcome and very useful.

Source: https://habr.com/ru/post/228719/


All Articles