[lời mở đầu: xin lỗi, có rất nhiều mã ở đây và một số mã có thể không liên quan đến câu hỏi này trong khi một số mã cần thiết để hiểu vấn đề có thể bị thiếu; vui lòng nhận xét và tôi sẽ chỉnh sửa câu hỏi tương ứng.]Làm thế nào để bạn giả lập một bộ công cụ JavaFX khởi tạo?
Môi trường: Ubuntu 14,10 x86_64; Oracle JDK 1.8u25. Thư viện kiểm thử đơn vị là TestNG, phiên bản 6.8.13; Mockito là phiên bản 1.10.17. Trong ứng dụng GUI của tôi, cái mà JavaFX gọi là "điều khiển" khá thụ động, theo nghĩa là điều duy nhất mà "bộ điều khiển" này (mà tôi gọi là "màn hình") thực sự là gửi các sự kiện.
Bây giờ, khi một sự kiện được nhận yêu cầu cập nhật GUI, nó là một lớp khác, mà tôi gọi là chế độ xem, chịu trách nhiệm cập nhật GUI. Nói tóm lại:
hiển thị -> người dẫn chương trình -> view -> hiển thị
tôi có các unit test cho hai trong số các:
- hiển thị -> dẫn chương trình;
- người trình bày -> chế độ xem.
Vì vậy, tôi được đề cập nhiều về mặt trước này (với lợi thế là tôi có thể thay đổi hiển thị, đó là lý do tôi làm theo cách đó).
Nhưng bây giờ tôi thử và kiểm tra phần "xem -> hiển thị"; và tôi là SOL.
Là một minh hoạ, đây là lớp xem:
@NonFinalForTesting
public class JavafxTreeTabView
extends JavafxView<TreeTabPresenter, TreeTabDisplay>
implements TreeTabView
{
private final BackgroundTaskRunner taskRunner;
public JavafxTreeTabView(final BackgroundTaskRunner taskRunner)
throws IOException
{
super("/tabs/treeTab.fxml");
this.taskRunner = taskRunner;
}
JavafxTreeTabView(final BackgroundTaskRunner taskRunner,
final Node node, final TreeTabDisplay display)
{
super(node, display);
this.taskRunner = taskRunner;
}
@Override
public void loadTree(final ParseNode rootNode)
{
taskRunner.compute(() -> buildTree(rootNode), value -> {
display.parseTree.setRoot(value);
display.treeExpand.setDisable(false);
});
}
@Override
public void loadText(final InputBuffer buffer)
{
final String text = buffer.extract(0, buffer.length());
display.inputText.getChildren().setAll(new Text(text));
}
@VisibleForTesting
TreeItem<ParseNode> buildTree(final ParseNode root)
{
return buildTree(root, false);
}
private TreeItem<ParseNode> buildTree(final ParseNode root,
final boolean expanded)
{
final TreeItem<ParseNode> ret = new TreeItem<>(root);
addChildren(ret, root, expanded);
return ret;
}
private void addChildren(final TreeItem<ParseNode> item,
final ParseNode parent, final boolean expanded)
{
TreeItem<ParseNode> childItem;
final List<TreeItem<ParseNode>> childrenItems
= FXCollections.observableArrayList();
for (final ParseNode node: parent.getChildren()) {
childItem = new TreeItem<>(node);
addChildren(childItem, node, expanded);
childrenItems.add(childItem);
}
item.getChildren().setAll(childrenItems);
item.setExpanded(expanded);
}
}
Lớp hiển thị phù hợp là thế này:
public class TreeTabDisplay
extends JavafxDisplay<TreeTabPresenter>
{
@FXML
protected Button treeExpand;
@FXML
protected TreeView<ParseNode> parseTree;
@FXML
protected TextFlow inputText;
@Override
public void init()
{
parseTree.setCellFactory(param -> new ParseNodeCell(presenter));
}
@FXML
void expandParseTreeEvent(final Event event)
{
}
private static final class ParseNodeCell
extends TreeCell<ParseNode>
{
private ParseNodeCell(final TreeTabPresenter presenter)
{
setEditable(false);
selectedProperty().addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(
final ObservableValue<? extends Boolean> observable,
final Boolean oldValue, final Boolean newValue)
{
if (!newValue)
return;
final ParseNode node = getItem();
if (node != null)
presenter.parseNodeShowEvent(node);
}
});
}
@Override
protected void updateItem(final ParseNode item, final boolean empty)
{
super.updateItem(item, empty);
setText(empty ? null : String.format("%s (%s)", item.getRuleName(),
item.isSuccess() ? "SUCCESS" : "FAILURE"));
}
}
}
và đây là tập tin thử nghiệm của tôi:
public final class JavafxTreeTabViewTest
{
private final Node node = mock(Node.class);
private final BackgroundTaskRunner taskRunner = new BackgroundTaskRunner(
MoreExecutors.newDirectExecutorService(), Runnable::run
);
private JavafxTreeTabView view;
private TreeTabDisplay display;
@BeforeMethod
public void init()
throws IOException
{
display = new TreeTabDisplay();
view = spy(new JavafxTreeTabView(taskRunner, node, display));
}
@Test
public void loadTreeTest()
{
final ParseNode rootNode = mock(ParseNode.class);
final TreeItem<ParseNode> item = mock(TreeItem.class);
doReturn(item).when(view).buildTree(same(rootNode));
display.parseTree = mock(TreeView.class);
display.treeExpand = mock(Button.class);
view.loadTree(rootNode);
verify(display.parseTree).setRoot(same(item));
verify(display.treeExpand).setDisable(false);
}
}
tôi dự kiến nó sẽ hoạt động ... Ngoại trừ việc nó không. Tuy nhiên "xa nhau" Tôi cố gắng tránh xa từ các mã nền tảng, ngay cả những lớp thử nghiệm trên không thành công với ngoại lệ này:
java.lang.ExceptionInInitializerError
at sun.reflect.GeneratedSerializationConstructorAccessor5.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:45)
at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
at org.mockito.internal.creation.instance.ObjenesisInstantiator.newInstance(ObjenesisInstantiator.java:14)
at org.mockito.internal.creation.cglib.ClassImposterizer.createProxy(ClassImposterizer.java:143)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:58)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49)
at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1285)
at org.mockito.Mockito.mock(Mockito.java:1163)
at com.github.fge.grappa.debugger.csvtrace.tabs.JavafxTreeTabViewTest.loadTreeTest(JavafxTreeTabViewTest.java:46)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
at org.testng.TestRunner.privateRun(TestRunner.java:767)
at org.testng.TestRunner.run(TestRunner.java:617)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:348)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:343)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:305)
at org.testng.SuiteRunner.run(SuiteRunner.java:254)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
at org.testng.TestNG.run(TestNG.java:1057)
at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:111)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:204)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:175)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:125)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:270)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:265)
at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:540)
at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:502)
at javafx.scene.control.Control.<clinit>(Control.java:87)
... 44 more
Vì vậy, trong ngắn hạn, làm thế nào để ngăn chặn sự ngoại lệ trên xảy ra? Tôi đã nghĩ rằng mocking các vật dụng đi là đủ, nhưng dường như không:/Có vẻ như tôi cần phải thử toàn bộ bối cảnh "nền tảng" (vì thiếu một từ tốt hơn cho nó) nhưng tôi không có ý tưởng làm thế nào.
Tôi không có kinh nghiệm với các công cụ cụ thể trong chuỗi công cụ của mình, vì vậy tôi không thể tư vấn cho bạn ở đó, nhưng bạn có thể áp dụng điều gì đó từ chiến lược được sử dụng cho [khởi tạo bộ công cụ JavaFX để kiểm tra đơn vị môi trường JUnit] (https://gist.github.com/andytill/3835914). – jewelsea
@jewelsea thú vị! Tôi sẽ cố gắng và điều chỉnh điều này để TestNG – fge
@jewelsea thích ứng thất bại, không may:/Tôi không thể làm cho nó hoạt động ... – fge