JavaFX 中的 JUnit 和多线程

JUnit and Multithreading in JavaFX

让我们有Analyzerclass提供耗时的analyze方法.

import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.concurrent.Task;

public class Analyzer {
    public enum State {
        NOT_ANALYZED, ANALYZING, ANALYZED, FAILED
    }

    private final ReadOnlyObjectWrapper<State> stateWrapper = new ReadOnlyObjectWrapper<>(State.NOT_ANALYZED);
    private volatile int result = -1;

    public void analyze(String someText) {
        synchronized(this) {
            if (stateWrapper.get() == State.ANALYZING)
                return;

            stateWrapper.set(State.ANALYZING);
        }

        Task analyzeWorker = new Task<Void>() {
            @Override
            protected Void call() {
                // Some time-consuming operations based on someText parameter

                result = 11;
                return null;
            }
        };

        analyzeWorker.setOnSucceeded(event -> stateWrapper.set(State.ANALYZED));
        analyzeWorker.setOnFailed(event -> stateWrapper.set(State.FAILED));

        (new Thread(analyzeWorker)).start();
    }

    public int getResult() {
        return result;
    }

    public ReadOnlyObjectProperty<State> stateProperty() {
        return stateWrapper.getReadOnlyProperty();
    }
}

analyze方法的调用者比听state属性就知道如果分析完成。

基于此,我想像这样编写 JUnit (4) 测试

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;

import org.junit.Test;
import static org.junit.Assert.*;

public class AnalyzerTest {
    @Test
    public void resultTest() {
        Object syncObject = new Object();
        Analyzer analyzer = new Analyzer();

        analyzer.stateProperty().addListener(
            new ChangeListener<Analyzer.State>() {
                @Override
                public void changed(ObservableValue<? extends Analyzer.State> observable, Analyzer.State oldValue, Analyzer.State newValue) {
                    if (newValue == Analyzer.State.ANALYZED) {
                        synchronized (syncObject) {
                            syncObject.notify();
                        }
                    }
                }
            }
        );

        synchronized(syncObject) {
            try {
                analyzer.analyze("Some long text");
                syncObject.wait();
            }
            catch (InterruptedException e) {
                fail("Intterupted Exception");
            }
        }
        assertEquals(11, analyzer.getResult());
    }
}

问题是这个测试永远不会结束。我在 analyzeWorker.call 方法 中放置了一个断点,在测试的情况下从未达到。

根据 James_D 的评论 – 在 test 的情况下,FX Application Thread 而不是 运行 自动且必须显式启动。

快速(而且肮脏)的方法:

public class AnalyzerTest {
    @Before
    public void setUp() {
        new JFXPanel();
    }

    @After
    public void tearDown() {
        Platform.exit();
    }

    @Test
    public void resultTest() {
        …
    }
}

在2次及以上测试的情况下,Platform.exit导致异常(java.lang.IllegalStateException: Platform.exit has been called),所以我不用它。