2012-11-20 206 views
3

我編寫了一個相當複雜的JavaFx 2應用程序,我想寫一堆單元測試。問題是當我嘗試進行測試時,我得到一個運行時錯誤,抱怨未初始化的工具包。使用TestNG測試JavaFx 2應用程序單元測試

從我能告訴我應該以某種方式調用@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 { 
    } 

提前感謝!

+1

您可能還需要看看JavaFX團隊用來測試他們的軟件的[JemmyFX visual testing framework](http://fxexperience.com/2012/02/announcing-jemmyfx/)。 – jewelsea

+2

JemmyFX的更好地址:http://jemmy.java.net/JemmyFXGuide/jemmy-guide.html – assylias

回答

2

another question這裏計算器,我自己做了這個小的輔助類:

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(); 
} 
+0

這與我想出的解決方案非常相似:) – Athelionas

0

好,考慮到你可能有你的JavaFX應用程序位於項目根/ src目錄/主/ JAVA /包/ FXApp.java,那麼你可能已經找到其他地方像項目根你的測試/ src目錄/測試/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參數,然後使用JVisualVM查看鎖定的線程。

1

主要考慮的是考慮在FX線程內運行測試。當你創建一個類擴展應用程序時,你實際上創建了一個過程。這是你想測試的。

因此,要在應用程序上啓動一些單元測試,首先創建一個擴展應用程序的FXAppTest,然後在FXAppTest中啓動單元測試。這是主意。

以下是JUnit的示例。我創建了一個在FXApp內部進行測試的Runner進行測試。 下面是對FxApplicationTest代碼(我們推出它裏面的單元測試)的一個例子

public class FxApplicationTest extends Application { 

    private volatile boolean isStopped; 

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

    public void startApp() { 
     launch(); 
    } 

    public void execute(final BlockJUnit4ClassRunner runner, final RunNotifier notifier) throws InterruptedException { 
     isStopped = false; 
     Platform.runLater(new Runnable() { 
      @Override 
      public void run() { 
       runner.run(notifier); 
       isStopped = true; 
      } 
     }); 
     while (!isStopped) { 
      Thread.sleep(100); 
     } 
    } 

和轉輪:

import fr.samarie_projects.fx.utils.JUnitFxRunner; 

@RunWith(JUnitFxRunner.class) 
public class MainFxAppTest { 

    @org.junit.Test 
    public void testName() throws Exception { 
     MainFxApp fxApp = new MainFxApp(); 
     fxApp.start(new Stage()); 
    } 

} 

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); 
    } 

} 

當然,這個代碼需要審查。只是提出這個想法。