Ожидание всех потоков, порожденных моим тестируемым кодом в тестовом примере JUnit
Как в тестовом примере JUnit убедиться, что все потоки, порожденные прямым / косвенным образом тестируемым методом, выполняются с заданием, чтобы я мог утверждать окончательный результат?
@Test
public void testMethod() {
Result result=method();// may spawn multiple threads to set result.value
Assert.assertTrue(result.getValue()==4); //should execute only after result.value is set to its final value.
}
3 ответа
Реальный вопрос в том, как вы справляетесь с этим случаем в своем не тестовом коде? Что такое API method()
гарантировать своим посетителям о том, когда result.value
будет установлен?
Имейте это в виду при написании тестов - цель состоит в том, чтобы утверждать, что класс и его методы ведут себя так, как они рекламируют. Иногда разработка того, что представляет собой рекламируемый интерфейс, может быть половиной проблемы.
В такой ситуации я настоятельно рекомендую вашему Result
объект ведет себя как Future
во что get()
метод блокируется, пока результат не будет доступен. (В качестве альтернативы, дайте ему waitFor()
метод или аналог).
Если ваш метод не предоставляет каких-либо конкретных гарантий или блокировок вызовов, все, что вы действительно можете сделать в этом тесте, это продолжать проверять значение каждые x секунд в цикле, помещая @Timeout
на тесте, чтобы убедиться, что значение установлено в "разумное" время. Но это все, что мог бы сделать клиент, поэтому
- это очень правильный тест;
- это подчеркивает, что интерфейс не очень удобен для клиентов, и изменение его было бы хорошей идеей.
Я предлагаю вам вызвать Thread#join для нескольких экземпляров потоков в методе (), следуйте этому, все дочерние потоки завершены.
Один подход для решения этой проблемы требует двух шагов:
- Блокировать основной тестовый поток во время ожидания рабочих потоков
- Пусть каждый рабочий поток сигнализирует о главном потоке, когда его работа завершена, разблокируя основной поток, когда последний рабочий завершает работу.
В идеале вы должны захотеть, чтобы основной поток завершил работу по тайм-ауту и провалил тест, если все работники не выполнят его в течение ожидаемого периода времени. Это довольно просто, используя ConcurrentUnit:
public class MyTest extends ConcurrentTestCase {
@Test
public void test() throws Throwable {
int threads = 5;
for (int i = 0; i < threads; i++) {
new Thread(new Runnable() {
public void run() {
threadAssertEquals(1, 1); // Perform assertions as needed
resume();
}
}).start();
}
threadWait(1000, threads); // Wait for 5 resume calls
}
}