JavaFX 中的 JUnit 和多线程
JUnit and Multithreading in JavaFX
让我们有Analyzer
class提供耗时的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),所以我不用它。
让我们有Analyzer
class提供耗时的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),所以我不用它。