Как jUnit проверить результат кода в другом потоке
У меня есть процесс, который выполняется в потоке (используется как процесс анализа сигнала в реальном времени). Я хочу передать этот поток процессу известному входу, а затем проверить - в jUnit - правильность вывода. У меня есть прослушиватель обратного вызова, который может уведомить меня, когда поток завершит обработку данных, и я могу успешно выполнить утверждения по результату, зарегистрировав сам тест как слушатель.
Когда эти утверждения терпят неудачу, они делают исключение. Но это исключение не регистрируется как сбой jUnit, по-видимому, потому, что они происходят вне метода тестирования.
Как структурировать мой тест jUnit, чтобы тест завершился с ошибкой после возвращения слушателя? Вот упрощенная версия кода.
public class PitchDetectionTest extends TestCase
implements EngineRunCompleteListener() {
AudioData fixtureData;
PitchDetectionEngine pitchEngine;
public void setUp() {
fixtureData = <stuff to load audio data>;
}
public void testCorrectPitch() {
pitchEngine = new PitchEngine(fixtureData);
pitchEngine.setCompletionListener(this);
pitchEngine.start();
// delay termination long enough for the engine to complete
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
}
// gets called by the PitchEngine when it has finished processing all data in the
// fixtureData. This is the only method defined by interface
// EngineRunCompleteListener.
public void notifyEngineRunComplete() {
// The real code asserts things about the PitchEngine results. When they fail,
// an exception is thrown that I can see in the console, but this unit test still
// shows as 'success' in the jUnit interface. What I want is to see
// testCorrectPitch() fail.
assertTrue(false);
}
}
public class PitchEngine () {
EngineRunCompleteListener completionListener();
Thread myThread;
public void start() {
// other stuff
myThread = new Thread(this);
myThread.start();
}
public void run() {
while (some condition) {
// do stuff with the data
}
if (engineRunCompleteListener != null) {
engineRunCompleteListener.notifyEngineRunComplete();
}
}
}
Ответы
Ответ 1
У вас уже есть два потока. Ваша нить junit и поток процесса (запущен myThread.start()
.
В верхней части моей головы я могу представить, по крайней мере, два варианта, которые у вас есть, и все они связаны с переносом утверждения от notifyEngineRunComplete
. Например:
- Вы можете использовать
join
, чтобы дождаться окончания процесса, а затем выполните ваши утверждения (Javadoc здесь).
- Вы можете поместить поток junit в режим ожидания, ожидая объекта монитора, а затем в вашей функции обратного вызова уведомьте об этом мониторе. Таким образом, вы узнаете, что процесс завершен.
- Вы можете использовать объект
Executor
и Future
. Я думаю, что это было бы самым крутым решением, если оно будет работать с вашими классами (Javadoc здесь).
Ответ 2
Общий подход к тестированию threaded/async-кода заключается в блокировании основного тестового потока, захвате любых неудачных утверждений из других потоков (таких как поток, вызывающий ваш слушатель), разблокирование основного тестового потока и сброса и неудачного утверждения. ConcurrentUnit делает это довольно просто:
final Waiter waiter = new Waiter();
bus.registerListener(() -> {
// Perform assertion in some other thread
waiter.assertTrue(true);
waiter.resume();
});
// Wait for resume() to be called
waiter.await(5000);
Ответ 3
Я хочу передать этот поток процессу известному входу, а затем проверить - в jUnit - правильность вывода. У меня есть прослушиватель обратного вызова, который может уведомить меня, когда поток завершит обработку данных, и я могу успешно выполнить утверждения по результату, зарегистрировав сам тест в качестве слушателя.
Вместо того, чтобы начинать отдельный поток для PitchEngine
внутри вашего unit test, почему бы не извлечь логику //do stuff with the data
в PitchEngine
в другой общедоступный метод и просто вызвать это в unit test? Я не могу придумать какой-либо причины фактически порождать поток внутри вашего unit test, поскольку он звучит так, как будто все, что вам действительно волнует (в этом unit test), проверяет логику обработки.
Ответ 4
если вы должны запустить код подтверждения в обратном вызове, оберните метод обратного вызова в try catch. поймать любой throwable и иметь способ передать это исключение обратно в поток junit (какое-то состояние общего потока). поток junit затем просто перебрасывает любые возвращаемые значения.
Ответ 5
join()
работает, если у вас есть только один рабочий поток, из которого вы хотите выполнить утверждения. Если у вас есть несколько потоков, которые должны сообщать о утверждениях в основной поток, вам нужно будет использовать другой механизм. Для этого ConcurrentUnit.