Статьи

Повышение производительности теста с помощью TestContainers

В моем предыдущем посте по тестированию я описал, как использовать TestContainers для обеспечения реалистичных сред тестирования для тестов базы данных. Этот комментарий выявил обратную сторону:

… как отмечалось выше, всегда есть какой-то недостаток. В этом случае затраты на запуск образа Docker и всего, что в нем содержится, увеличат общее время сборки.

В качестве напоминания, вот код, специфичный для TestContainer. Обратите внимание на экземпляр члена postgres и Rule JUnit, которое повторно инициализирует его для каждого метода.

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);
    }
}

Принимая во внимание, что значительное увеличение продолжительности теста является результатом времени запуска Docker-контейнера, мы можем вместо этого использовать JUnit ClassRule чтобы запустить один контейнер и повторно использовать его для каждого теста в классе. Это означает, что вы больше не должны запускать эти тесты параллельно, но производительность значительно перевешивает параллелизацию тестов.

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);
    }
}

Количество сэкономленного времени зависит от количества методов тестирования в классе. У меня есть несколько тестовых классов, каждый из которых имеет более 30 тестов, и в этих случаях время выполнения падает с минут до секунд. Неплохо для смены пары строк кода.