Unit testing 使用TestNG对JavaFX2应用程序进行单元测试

Unit testing 使用TestNG对JavaFX2应用程序进行单元测试,unit-testing,javafx-2,testng,Unit Testing,Javafx 2,Testng,我编写了一个相当复杂的JavaFX2应用程序,我想为它编写一系列单元测试。问题是,当我尝试进行测试时,我收到一个运行时错误,抱怨工具箱未初始化 据我所知,我应该以某种方式调用@BeforeClass方法中的Application.launch(),但这会导致死锁,因为Application.launch()不会返回调用线程 所以问题是我应该如何初始化JavaFx 这是不起作用的代码的框架: public class AppTest extends Application { @Befo

我编写了一个相当复杂的JavaFX2应用程序,我想为它编写一系列单元测试。问题是,当我尝试进行测试时,我收到一个运行时错误,抱怨工具箱未初始化

据我所知,我应该以某种方式调用@BeforeClass方法中的Application.launch(),但这会导致死锁,因为Application.launch()不会返回调用线程

所以问题是我应该如何初始化JavaFx

这是不起作用的代码的框架:

public class AppTest extends Application {

    @BeforeClass
    public void initialize() {
        launch(); //this causes a deadlock
    }

    @Test
    public void test1() {
        //conduct test here
    }

    @Test
    public void test2() {
        //conduct other test here
    }

    @Override
    public void start(Stage arg0) throws Exception {
    }

提前谢谢

好吧,考虑到您的JavaFX应用程序可能位于项目根/src/main/java/package/FXApp.java,那么您的测试可能位于其他位置,如项目根/src/test/java/package/FXAppTest.java。在这种情况下,FXAppTest类可以通过使用BeforeClass初始化FXApp类来调用它

从理论上讲,您应该能够通过以下方式启动您的FX应用程序:

// imports located here that import junit 4.11+ packages (or TestNG)
public class FXAppTest {
@BeforeClass
public void initialize() {
   FXApp fxa = new FXApp();
   while ( fxa.isLoading() ) {
      // do nothing
   }
}
....
注意:请注意,FXAppTest不会在此处扩展应用程序


现在,如果这不能提示您进入这个问题,您可以在JVM上启用JMX ARG,然后用JVisualVM查看锁定的线程。

主要考虑的是在FX线程内运行您的测试。当您创建一个类扩展应用程序时,实际上您创建了一个进程。这就是您要测试的内容

所以,要在应用程序上启动一些单元测试,首先创建一个扩展应用程序的FXAppTest,然后在FXAppTest内部启动单元测试。这是我的想法

下面是一个JUnit示例。我创建了一个运行程序,在FXApp中启动测试以进行测试。 下面是FxApplicationTest的代码示例(我们在其中启动单元测试)

跑步者:

import org.apache.log4j.Logger;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;

public class JUnitFxRunner extends Runner {

    private final BlockJUnit4ClassRunner runner;
    private final Logger LOGGER = Logger.getLogger(JUnitFxRunner.class);

    public JUnitFxRunner(final Class<?> klass) throws InitializationError {
        super();
        runner = new BlockJUnit4ClassRunner(klass);
    }

    @Override
    public Description getDescription() {
        return Description.EMPTY;
    }

    @Override
    public void run(final RunNotifier notifier) {
        try {

            final FxApplicationTest fxApplicationTest = new FxApplicationTest();

            MyTestRunner runnable = new MyTestRunner(runner, notifier, fxApplicationTest);
            new Thread(runnable).start();
            Thread.sleep(100);
            runnable.execute();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }

    }

    private class MyTestRunner implements Runnable {

        private final BlockJUnit4ClassRunner runner;
        private final RunNotifier notifier;
        private final FxApplicationTest fxApp;

        public MyTestRunner(final BlockJUnit4ClassRunner runner, final RunNotifier notifier, final FxApplicationTest fxApp) {
            this.runner = runner;
            this.notifier = notifier;
            this.fxApp = fxApp;
        }

        @Override
        public void run() {
            fxApp.startApp();
        }

        public void execute() throws InterruptedException {
            fxApp.execute(runner, notifier);
        }
    }

}
此单元测试MainFxApp

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class MainFxApp extends Application {

    @Override
    public void start(final Stage stage) throws Exception {
        StackPane root = new StackPane();
        Scene scene = new Scene(root, 10, 10);
        stage.setScene(scene);
    }

    public static void main(final String[] args) {
        launch(args);
    }

}
当然,这段代码需要检查。这只是为了表达这个想法

从stackoverflow开始,我创建了一个小助手类:

import javafx.application.Application;
import javafx.stage.Stage;

public class JavaFXInitializer extends Application {

    private static Object barrier = new Object();

    @Override
    public void start(Stage primaryStage) throws Exception {
        synchronized(barrier) {
            barrier.notify();
        }
    }

    public static void initialize() throws InterruptedException {
        Thread t = new Thread("JavaFX Init Thread") {
            public void run() {
                Application.launch(JavaFXInitializer.class, new String[0]);
            }
        };
        t.setDaemon(true);
        t.start();      
        synchronized(barrier) {
            barrier.wait();
        }
    }
}
然后可以在@BeforeClass设置方法中轻松使用:

@BeforeClass
public void setup() throws InterruptedException {
    JavaFXInitializer.initialize();
}

您可能还想看看JavaFX团队用来测试其软件的方法
import javafx.application.Application;
import javafx.stage.Stage;

public class JavaFXInitializer extends Application {

    private static Object barrier = new Object();

    @Override
    public void start(Stage primaryStage) throws Exception {
        synchronized(barrier) {
            barrier.notify();
        }
    }

    public static void initialize() throws InterruptedException {
        Thread t = new Thread("JavaFX Init Thread") {
            public void run() {
                Application.launch(JavaFXInitializer.class, new String[0]);
            }
        };
        t.setDaemon(true);
        t.start();      
        synchronized(barrier) {
            barrier.wait();
        }
    }
}
@BeforeClass
public void setup() throws InterruptedException {
    JavaFXInitializer.initialize();
}