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.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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. |