Как ждать потока, который порождает свой собственный поток?

Я пытаюсь протестировать метод, который работает в отдельном потоке, упрощенно это так:

public void methodToTest()
{
    Thread thread = new Thread()
    {
        @Override
        public void run() {
            Clazz.i = 2;
        }
    };
    thread.start();
}

В моем модульном тесте я хочу проверить это Clazz.i == 2, но я не могу этого сделать, потому что я думаю, что assert запускается до того, как поток изменяет значение. Я подумал об использовании другого потока, чтобы проверить его, а затем использовать соединение, чтобы ждать, но он все еще не работает.

SSCCE:

@Test
public void sscce() throws InterruptedException
{
    Thread thread = new Thread()
    {
        @Override
        public void run() {
            methodToTest()
        }
    };
    thread.start();     
    thread.join();  
    AssertEquals(2, Clazz.i);
}

public static class Clazz
{
    public static int i = 0;
}

Я думаю, это потому, что основной тестовый код создает поток, который ожидает (присоединился) ко 2-му потоку, но 2-й поток не выполняет работу, он создает другой поток для выполнения работы, а затем завершает работу, которая продолжает первый поток, в то время как третий поток делает Clazz.i = 2 после утверждения.

Как сделать так, чтобы первый поток ожидал запускаемого потока, а также всех потоков, запускаемых этим потоком?

3 ответа

Без ссылки на тему, созданную в methodToTest Вы не можете, просто. Java не предоставляет механизма для поиска "потоков, которые были порождены в этот конкретный период времени" (и даже если бы это было так, это, возможно, было бы уродливым механизмом для использования).

На мой взгляд, у вас есть два варианта:

  • Делать methodToTest ждать нити, которую это порождает. Конечно, если вы явно хотите, чтобы это было асинхронное действие, то вы не можете сделать это очень хорошо.
  • Вернуть вновь созданную тему из methodToTest, так что любой вызывающий абонент может дождаться его, если они того пожелают.

Можно отметить, что второй вариант может быть сформулирован несколькими различными способами. Вы могли бы, например, вернуть некоторые абстрактные Future -подобный объект, а не поток, если вы хотите расширить свободу methodToTest использовать различные способы выполнения асинхронной работы. Возможно, вы могли бы также определить некоторый глобальный пул задач, в котором вы принудительно выполняете все свои асинхронные задачи, а затем дождаться завершения всех задач в пуле, прежде чем проверять утверждение. Такой пул задач может принимать форму ExecutorService или ThreadGroup или любое количество других форм. Все они в конце концов делают одно и то же, но могут более или менее подходить для вашей среды - главное, что вы должны явно предоставить вызывающей стороне доступ к вновь созданному потоку, так или иначе.

Поскольку ваши потоки, кажется, выполняют разные операции, вы можете использовать CountDownLatch для решения вашей проблемы.

Объявить CountDownLatch в главном потоке и передать этот объект защелки другим потокам. используйте await() в основном потоке и уменьшите фиксатор в других потоках.

В основной теме: (первая тема)

CountDownLatch latch = new CountDownLatch(2);
/* Create Second thread and pass the latch. Pass the same latch from second 
   thread to third thread when you are creating third thread */
try {
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}

Передайте эту защелку второму и третьему потокам и используйте обратный отсчет в этих потоках

Во втором и третьем потоках,

try {
    // add your business logic i.e. run() method implementation
    latch.countDown();
} catch (InterruptedException e) {
    e.printStackTrace();
}

Посмотрите эту статью для лучшего понимания.

ExecutorService invokeAll() API является другим предпочтительным решением.

Вы не можете проверить функциональность модуля, которую не обеспечивает модуль.

Вы говорите, что хотите это проверить methodToTest() в конце концов устанавливает Clazz.i=2, но что значит "в конце концов"? Ваш methodToTest() Функция не предоставляет вызывающей стороне способ узнать, когда Clazz.i был установлен. Причина, по которой вам трудно понять, как протестировать эту функцию, заключается в том, что ваш модуль не предоставляет эту функцию.

Возможно, сейчас самое время прочитать о разработке через тестирование (TDD). Здесь вы сначала пишете тесты, а затем пишете код, который делает тесты успешными. Написание тестов сначала поможет вам нарисовать более четкую картину того, что вы хотите, чтобы модуль делал.

Это также имеет дополнительное преимущество: если вы практикуете строгий TDD (то есть, если вы никогда не пишете какой-либо код модуля, кроме как для прохождения теста), то ваш охват тестированием составит 100%.

И это приводит к другому побочному эффекту: если у вас 100% тестовое покрытие, вы можете без страха проводить рефакторинг, потому что если вы что-то сломаете, ваши модульные тесты скажут вам об этом.

Другие вопросы по тегам