Boosting test performance with TestContainers
In my previous post on testing, I described how to use TestContainers to provide realistic test environments for database tests. This comment revealed the downside:
…as noted above, there always seems to be some drawback. In this case, the overhead of starting the Docker image and everything it contains will increase your overall build time.
As a reminder, here’s the TestContainer-specific code. Note the instance member postgres, and the JUnit Rule that re-initializes it on a per-method basis.
package be.objectify.tcexample.db;
import be.objectify.tcexample.AbstractUserDaoTest;
import be.objectify.tcexample.UserDao;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.testcontainers.containers.PostgreSQLContainer;
import play.db.Database;
public class JooqUserDaoTest extends AbstractUserDaoTest implements DbTestSupport,
TestData {
@Rule
public PostgreSQLContainer postgres = new PostgreSQLContainer();
private Database database;
@Before
public void setup() throws Exception {
// the database has all evolutions applied
database = create(postgres);
// load some test data
loadTestData(database);
}
@After
public void tearDown() {
destroy(database);
}
@Override
public UserDao dao() {
return new JooqUserDao(database);
}
}Given that the huge increase in test duration results from Docker container start-up times, we can instead use a JUnit ClassRule to start up one container and re-use it for every test in the class. This means you should no longer run these tests in parallel, but the performance gains massively outweigh test parallelization.
package be.objectify.tcexample.db;
import be.objectify.tcexample.AbstractUserDaoTest;
import be.objectify.tcexample.UserDao;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.testcontainers.containers.PostgreSQLContainer;
import play.db.Database;
public class FasterJooqUserDaoTest extends AbstractUserDaoTest implements DbTestSupport,
TestData {
@ClassRule
public static PostgreSQLContainer postgres = new PostgreSQLContainer();
private Database database;
@Before
public void setup() throws Exception {
database = create(postgres);
loadTestData(database);
}
@After
public void tearDown() {
destroy(database);
}
@Override
public UserDao dao() {
return new JooqUserDao(database);
}
}The amount of time saved depends on the number of test methods in a class. I have some test classes that have upwards of 30 tests each, and in these cases the execution time drops from minutes to seconds. Not bad for changing a couple of lines of code.
| Reference: | Boosting test performance with TestContainers from our JCG partner Steve Chaloner at the Objectify blog. |




