如何模拟JavaFX工具包初始化?

如何模拟JavaFX工具包初始化?,java,unit-testing,javafx,testng,mockito,Java,Unit Testing,Javafx,Testng,Mockito,[序言:抱歉,这里有很多代码,其中一些代码可能与此问题无关,而理解此问题所需的一些代码可能缺失;请评论,我将相应地编辑此问题。] 环境:Ubuntu 14.10 x86_64;OracleJDK1.8u25。单元测试库是TestNG,版本6.8.13;Mockito的版本是1.10.17 在我的GUI应用程序中,JavaFX所称的“控制器”是相当被动的,因为这个“控制器”(我称之为“显示器”)真正做的唯一事情就是发送事件 现在,当接收到需要GUI更新的事件时,它是另一个类,我称之为视图,负责更新

[序言:抱歉,这里有很多代码,其中一些代码可能与此问题无关,而理解此问题所需的一些代码可能缺失;请评论,我将相应地编辑此问题。]

环境:Ubuntu 14.10 x86_64;OracleJDK1.8u25。单元测试库是TestNG,版本6.8.13;Mockito的版本是1.10.17

在我的GUI应用程序中,JavaFX所称的“控制器”是相当被动的,因为这个“控制器”(我称之为“显示器”)真正做的唯一事情就是发送事件

现在,当接收到需要GUI更新的事件时,它是另一个类,我称之为视图,负责更新GUI。简言之:

显示->演示者->查看->显示

我对其中两个进行了单元测试:

  • 显示->演示者
  • 演示者->查看
因此,我在这方面做了很多工作(优点是我可以改变显示,这就是我为什么这样做的原因)

但是现在我尝试测试“查看->显示”部分;我是索尔

作为说明,以下是视图类:

@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);
    }
}
@NonFinalForTesting
公共类JavafxTreeTabView
扩展JavafxView
实现树视图
{
私人最终后台任务执行者任务执行者;
公共JavafxTreeTabView(最终背景taskRunner taskRunner)
抛出IOException
{
super(“/tabs/treeTab.fxml”);
this.taskRunner=taskRunner;
}
JavafxTreeTabView(最终背景taskRunner taskRunner,
最终节点,最终树B显示(显示)
{
超级(节点、显示器);
this.taskRunner=taskRunner;
}
@凌驾
公共void加载树(最终解析节点rootNode)
{
taskRunner.compute(()->buildTree(rootNode),值->{
display.parseTree.setRoot(值);
display.treeExpand.setDisable(false);
});
}
@凌驾
公共void加载文本(最终输入缓冲区)
{
最后一个字符串text=buffer.extract(0,buffer.length());
display.inputText.getChildren().setAll(新文本(Text));
}
@可视性测试
TreeItem buildTree(最终解析节点根)
{
返回buildTree(root,false);
}
私有树Item buildTree(最终解析节点根,
最终布尔值(扩展)
{
最终TreeItem ret=新TreeItem(根);
addChildren(ret、root、expanded);
返回ret;
}
私有void addChildren(最终树项,
最终解析节点父节点,最终布尔值(展开)
{
TreeItem子项;
儿童项目最终清单
=FXCollections.observableArrayList();
for(最终ParseNode节点:parent.getChildren()){
childItem=新树项(节点);
添加子项(子项、节点、展开);
添加(childItem);
}
item.getChildren().setAll(childrenItems);
项目.扩大(扩大);
}
}
匹配的显示类如下所示:

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"));
        }
    }
}
公共类TreeTabDisplay
扩展JavafxDisplay
{
@FXML
受保护的按钮树扩展面板;
@FXML
受保护的树景树;
@FXML
受保护的文本流输入文本;
@凌驾
公共void init()
{
setCellFactory(参数->新建ParseNodeCell(演示者));
}
@FXML
void expandParseTreeEvent(最终事件)
{
}
私有静态最终类ParseNodeCell
扩展TreeCell
{
private ParseNodeCell(最终TreeTabPresenter演示者)
{
可编辑设置(假);
selectedProperty().addListener(新的ChangeListener())
{
@凌驾
更改公众假期(

最终观察值好的,第一件事:我一生中从未使用过Mockito。但我很好奇,所以我花了几个小时来解决这个问题,我想还有很多地方需要改进

因此,要使这项工作发挥作用,我们需要:

  • 自定义
    MockMaker
    实现,包装默认的
    CglibMockMaker
  • 把这些东西连接起来
  • 那么1+2是这样的:

    public class JavaFXMockMaker implements MockMaker {
    
        private final MockMaker wrapped = new CglibMockMaker();
        private boolean jfxIsSetup;
    
        private void doOnJavaFXThread(Runnable pRun) throws RuntimeException {
            if (!jfxIsSetup) {
                setupJavaFX();
                jfxIsSetup = true;
            }
            final CountDownLatch countDownLatch = new CountDownLatch(1);
            Platform.runLater(() -> {
                pRun.run();
                countDownLatch.countDown();
            });
    
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    
        protected void setupJavaFX() throws RuntimeException {
            final CountDownLatch latch = new CountDownLatch(1);
            SwingUtilities.invokeLater(() -> {
                new JFXPanel(); // initializes JavaFX environment
                latch.countDown();
            });
    
            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
            AtomicReference<T> result = new AtomicReference<>();
            Runnable run = () -> result.set(wrapped.createMock(settings, handler));
            doOnJavaFXThread(run);
            return result.get();
        }
    
        @Override
        public MockHandler getHandler(Object mock) {
            AtomicReference<MockHandler> result = new AtomicReference<>();
            Runnable run = () -> result.set(wrapped.getHandler(mock));
            doOnJavaFXThread(run);
            return result.get();
        }
    
        @Override
        public void resetMock(Object mock, MockHandler newHandler, @SuppressWarnings("rawtypes") MockCreationSettings settings) {
            Runnable run = () -> wrapped.resetMock(mock, newHandler, settings); 
            doOnJavaFXThread(run);
        }
    
    }
    
    公共类JavaFXMockMaker实现MockMaker{
    private final MockMaker wrapped=new CglibMockMaker();
    私有布尔jfxIsSetup;
    私有void doOnJavaFXThread(Runnable pRun)引发RuntimeException{
    如果(!jfxIsSetup){
    setupJavaFX();
    jfxIsSetup=true;
    }
    最终倒计时锁存器倒计时锁存器=新倒计时锁存器(1);
    Platform.runLater(()->{
    pRun.run();
    countdownlock.countDown();
    });
    试一试{
    倒计时闩锁。等待();
    }捕捉(中断异常e){
    抛出新的运行时异常(e);
    }
    }
    受保护的void setupJavaFX()引发RuntimeException{
    最终倒计时闩锁=新倒计时闩锁(1);
    SwingUtilities.invokeLater(()->{
    新建JFXPanel();//初始化JavaFX环境
    倒计时();
    });
    试一试{
    satch.wait();
    }捕捉(中断异常e){
    抛出新的运行时异常(e);
    }
    }
    @凌驾
    公共T createMock(MockCreationSettings,MockHandler){
    AtomicReference结果=新的AtomicReference();
    Runnable run=()->result.set(wrapped.createMock(settings,handler));
    doOnJavaFXThread(运行);
    返回result.get();
    }
    @凌驾
    公共MockHandler getHandler(对象模拟){
    AtomicReference结果=新的AtomicReference();
    Runnable run=()->result.set(wrapped.getHandler(mock));
    doOnJavaFXThread(运行);
    返回result.get();
    }
    @凌驾
    public void resetMock(对象mock、MockHandler newHandler、@SuppressWarnings(“rawtypes”)MockCreationSettings设置){
    朗纳布尔
    
    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
    
    public class JavaFXMockMaker implements MockMaker {
    
        private final MockMaker wrapped = new CglibMockMaker();
        private boolean jfxIsSetup;
    
        private void doOnJavaFXThread(Runnable pRun) throws RuntimeException {
            if (!jfxIsSetup) {
                setupJavaFX();
                jfxIsSetup = true;
            }
            final CountDownLatch countDownLatch = new CountDownLatch(1);
            Platform.runLater(() -> {
                pRun.run();
                countDownLatch.countDown();
            });
    
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    
        protected void setupJavaFX() throws RuntimeException {
            final CountDownLatch latch = new CountDownLatch(1);
            SwingUtilities.invokeLater(() -> {
                new JFXPanel(); // initializes JavaFX environment
                latch.countDown();
            });
    
            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
            AtomicReference<T> result = new AtomicReference<>();
            Runnable run = () -> result.set(wrapped.createMock(settings, handler));
            doOnJavaFXThread(run);
            return result.get();
        }
    
        @Override
        public MockHandler getHandler(Object mock) {
            AtomicReference<MockHandler> result = new AtomicReference<>();
            Runnable run = () -> result.set(wrapped.getHandler(mock));
            doOnJavaFXThread(run);
            return result.get();
        }
    
        @Override
        public void resetMock(Object mock, MockHandler newHandler, @SuppressWarnings("rawtypes") MockCreationSettings settings) {
            Runnable run = () -> wrapped.resetMock(mock, newHandler, settings); 
            doOnJavaFXThread(run);
        }
    
    }