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::withInitScriptAdvantage 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 {
//...
}
JDBCIt 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...
}
...
}