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. |