Как ограничить параллелизм в тестах junit 5

Краткое описание - тесты junit 5 работают на встроенной многопоточке. В каждом создаются свои юзеры(экземляры определенного класса, являющегося ядром структуры) и над ним проводится тестирование. Пришла задача ограничить пул юзеров в 20 штук. Т.е. теперь все тесты будут "цеплять" юзеров из пула и работать с ними. Дело в том, что тестов много больше чем юзеров, да и бывает что в тесте используется 2 юзера. В общем подскажите библиотеку, которая это разрулит, не хочется самому писать) Надо чтобы на юзера, подхваченного тестом, накладывался лок до завершения теста и если свободных юзеров нет в наличии, чтобы тест ждал свободного юзера.


Ответы (1 шт):

Автор решения: Roman Konoval

Сначала задаем конфигурацию для junit5:

junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = concurrent
junit.jupiter.execution.parallel.config.strategy = fixed
junit.jupiter.execution.parallel.config.fixed.parallelism = 20

Это даст гарантию, что больше 20 потоков junit не запустит.

Далее нужно реализовать глобальный пул пользователей. Каждый тест перед запуском создает нужных ему пользователей с помощью пула. После окончания - тест возвращает пользователей в пул.

И самому реализовать несложно, а можно воспользоваться готовым из apache commons-poool.

Если у важ уже есть список объектов, то вам нужно реализовать метод makeObject из PooledObjectFactory, который будет возвращать готовые объекты один раз. Я не даю готовую реализацию и не тестровал это код, это скорее демонстрация, которая показывает, как эти приблизительно выглядит. Что-то типа :

class PrecreatedPooledObjectFactory extends BasePooledObjectFactory<User>{
  List<User> users;
  PrecreatedPoolObjectFactory(List<User> users) {
    this.user = user;
  }
  public PooledObject<User> makeObject() {
    User user = users.remove(user.get(0);
    return new DefaultPooledObject<User>(user);
  }
}

GenericObjectPoolConfig<User> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(20);
List<User> precreatedUsers = ...
GenericObjectPool<User> pool = new GenericObjectPool<>(
   new PrecreatedPooledObjectFactory(precreatedUsers),
   config
);

И далее используете:

@BeforeEach
void setUp() {
  user = pool.borrowObject();
}

@AfterEach
void tearDown() {
  pool.returnObject(user);
}

Когда в пуле заканчиваются объекты, тест будет ждать в setUp пока другой тест не вернет их в пул.

→ Ссылка