I am testing TestContainers and I would like to know how to populate a database executing a .sql file to create the structure and add some rows.
How to do it?
JdbcDatabaseContainer::withInitScript
Advantage of this solution is that script is run before Spring Application Context
loads (at least when it is in a static block) and the code is quite simple.
Example:
static {
postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.8")
.withDatabaseName("integration-tests-db")
.withUsername("sa")
.withPassword("sa");
postgreSQLContainer
.withInitScript("some/location/on/classpath/someScript.sql");
postgreSQLContainer.start();
}
JdbcDatabaseContainer
is superclass of PostgreSQLContainer
so this solution should work not only for postgres
, but also for other containers.
Example:
static {
postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.8")
.withDatabaseName("integration-tests-db")
.withUsername("sa")
.withPassword("sa");
postgreSQLContainer.start();
var containerDelegate = new JdbcDatabaseDelegate(postgreSQLContainer, "");
ScriptUtils.runInitScript(containerDelegate, "some/location/on/classpath/someScriptFirst.sql");
ScriptUtils.runInitScript(containerDelegate, "some/location/on/classpath/someScriptSecond.sql");
ScriptUtils.runInitScript(containerDelegate, "ssome/location/on/classpath/someScriptThird.sql");
}
@Sql
annotation@SpringBootTest
@Sql("some/location/on/classpath/someScriptFirst.sql", "some/location/on/classpath/someScriptSecond.sql")
public class SomeTest {
//...
}
ResourceDatabasePopulator
from jdbc.datasource.init
or r2dbc.connection.init
when using JDBC
or R2DBC
consecutivelyclass DbInitializer {
private static boolean initialized = false;
@Autowired
void initializeDb(ConnectionFactory connectionFactory) {
if (!initialized) {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource[] scripts = new Resource[] {
resourceLoader.getResource("classpath:some/location/on/classpath/someScriptFirst.sql"),
resourceLoader.getResource("classpath:some/location/on/classpath/someScriptSecond.sql"),
resourceLoader.getResource("classpath:some/location/on/classpath/someScriptThird.sql")
};
new ResourceDatabasePopulator(scripts).populate(connectionFactory).block();
initialized = true;
}
}
}
@SpringBootTest
@Import(DbInitializer.class)
public class SomeTest {
//...
}
JDBC
It is mentioned in offical Testcontainers
documentation:
https://www.testcontainers.org/modules/databases/jdbc/
Classpath file:
jdbc:tc:postgresql:9.6.8:///databasename?TC_INITSCRIPT=somepath/init_mysql.sql
File that is not on classpath, but its path is relative to the working directory, which will usually be the project root:
jdbc:tc:postgresql:9.6.8:///databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql
Using an init function:
jdbc:tc:postgresql:9.6.8:///databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction
package org.testcontainers.jdbc;
public class JDBCDriverTest {
public static void sampleInitFunction(Connection connection) throws SQLException {
// e.g. run schema setup or Flyway/liquibase/etc DB migrations here...
}
...
}