2017-10-10 237 views
1

使用Swing开发Java桌面应用程序时,我遇到了直接测试UI的需求,而不仅仅是通过单元测试来测试底层控制器/模型类。如何开始:使用AssertJ Swing测试Java Swing GUI

这个answer (on "What is the best testing tool for Swing-based applications?")建议使用FEST,这是不幸中止。 但是,还有一些项目从FEST离开的地方继续。其中一个(在answer中提到)引起了我的注意,正如我之前在单元测试中使用的那样:AssertJ

显然有AssertJ Swing,这是基于FEST,并提供了一些易于使用的方式来编写您的Swing UI测试。 但是,进入初始/工作设置很麻烦,因为很难说从哪里开始。


如何创建一个最小的测试设置下面的示例UI,仅由两个班的?

约束:Java SE的,Swing的UI,Maven项目,JUnit的

public class MainApp { 

    /** 
    * Run me, to use the app yourself. 
    * 
    * @param args ignored 
    */ 
    public static void main(String[] args) { 
     MainApp.showWindow().setSize(600, 600); 
    } 

    /** 
    * Internal standard method to initialize the view, returning the main JFrame (also to be used in automated tests). 
    * 
    * @return initialized JFrame instance 
    */ 
    public static MainWindow showWindow() { 
     MainWindow mainWindow = new MainWindow(); 
     mainWindow.setVisible(true); 
     return mainWindow; 
    } 
} 

public class MainWindow extends JFrame { 

    public MainWindow() { 
     super("MainWindow"); 
     this.setContentPane(this.createContentPane()); 
    } 

    private JPanel createContentPane() { 
     JTextArea centerArea = new JTextArea(); 
     centerArea.setName("Center-Area"); 
     centerArea.setEditable(false); 
     JButton northButton = this.createButton("North", centerArea); 
     JButton southButton = this.createButton("South", centerArea); 
     JPanel contentPane = new JPanel(new BorderLayout()); 
     contentPane.add(centerArea); 
     contentPane.add(northButton, BorderLayout.NORTH); 
     contentPane.add(southButton, BorderLayout.SOUTH); 
     return contentPane; 
    } 

    private JButton createButton(final String text, final JTextArea centerArea) { 
     JButton button = new JButton(text); 
     button.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent event) { 
       centerArea.setText(centerArea.getText() + text + ", "); 
      } 
     }); 
     return button; 
    } 
} 

我知道,这个问题本身是非常广泛的,因此,我提供一个答案我自己 - 显示这个特殊的例子。

+1

此问题解答是为了回应这个答案的评论中的请求而创建的:https://stackoverflow.com/a/80222/5127499 – Carsten

回答

1

TL; DR:示例项目可在GitHub上找到。


假设这是一个Maven项目,你首先需要添加至少两个依赖:

  1. 单元测试框架(如这里junit - 但也可以使用testng
  2. 匹配AssertJ Swing库(如这里assertj-swing-junit

它可能看起来升IKE在此(在你pom.xml

<dependency> 
    <groupId>junit</groupId> 
    <artifactId>junit</artifactId> 
    <version>4.12</version> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>org.assertj</groupId> 
    <artifactId>assertj-swing-junit</artifactId> 
    <version>1.2.0</version> 
    <scope>test</scope> 
</dependency> 

其次,我经常去一个基础测试类,从实际测试中分离大多数测试设置:

/** 
* Base class for all my UI tests taking care of the basic setup. 
*/ 
public class AbstractUiTest extends AssertJSwingTestCaseTemplate { 

    /** 
    * The main entry point for any tests: the wrapped MainWindow. 
    */ 
    protected FrameFixture frame; 

    /** 
    * Installs a {@link FailOnThreadViolationRepaintManager} to catch violations of Swing threading rules. 
    */ 
    @BeforeClass 
    public static final void setUpOnce() { 
     // avoid UI test execution in a headless environment (e.g. when building in CI environment like Jenkins or TravisCI) 
     Assume.assumeFalse("Automated UI Test cannot be executed in headless environment", GraphicsEnvironment.isHeadless()); 
     FailOnThreadViolationRepaintManager.install(); 
    } 

    /** 
    * Sets up this test's fixture, starting from creation of a new <code>{@link Robot}</code>. 
    * 
    * @see #setUpRobot() 
    * @see #onSetUp() 
    */ 
    @Before 
    public final void setUp() { 
     // call provided AssertJSwingTestCaseTemplate.setUpRobot() 
     this.setUpRobot(); 
     // initialize the graphical user interface 
     MainWindow mainWindow = GuiActionRunner.execute(new GuiQuery<MainWindow>() { 

      @Override 
      protected MainWindow executeInEDT() throws Exception { 
       return MainApp.showWindow(); 
      } 
     }); 
     this.frame = new FrameFixture(this.robot(), mainWindow); 
     this.frame.show(); 
     this.frame.resizeTo(new Dimension(600, 600)); 
     onSetUp(); 
    } 

    /** 
    * Subclasses that need to set up their own test fixtures in this method. Called as <strong>last action</strong> during {@link #setUp()}. 
    */ 
    protected void onSetUp() { 
     // default: everything is already set up 
    } 

    /***************************************************************************************** 
    * Here you could insert further helper methods, e.g. frequently used component matchers * 
    *****************************************************************************************/ 

    /** 
    * Cleans up any resources used in this test. After calling <code>{@link #onTearDown()}</code>, this method cleans up resources used by this 
    * test's <code>{@link Robot}</code>. 
    * 
    * @see #cleanUp() 
    * @see #onTearDown() 
    */ 
    @After 
    public final void tearDown() { 
     try { 
      onTearDown(); 
      this.frame = null; 
     } finally { 
      cleanUp(); 
     } 
    } 

    /** 
    * Subclasses that need to clean up resources can do so in this method. Called as <strong>first action</strong> during {@link #tearDown()}. 
    */ 
    protected void onTearDown() { 
     // default: nothing more to tear down 
    } 
} 

实际测试类可能看起来像这则:

public class MainWindowTest extends AbstractUiTest { 

    private JButtonFixture northButtonFixture; 
    private JButtonFixture southButtonFixture; 

    @Override 
    protected void onSetUp() { 
     this.northButtonFixture = this.frame.button(JButtonMatcher.withText("North")); 
     this.southButtonFixture = this.frame.button(JButtonMatcher.withText("South")); 
    } 

    @Test 
    public void testWithDifferingComponentMatchers() { 
     // use JTextComponentMatcher.any() as there is only one text input 
     this.frame.textBox(JTextComponentMatcher.any()).requireVisible().requireEnabled().requireNotEditable().requireEmpty(); 
     this.northButtonFixture.requireVisible().requireEnabled().click(); 
     // use value assigned in MainWindow class via JTextArea.setName("Center-Area") to identify component here 
     this.frame.textBox("Center-Area").requireText("North, "); 

     this.southButtonFixture.requireVisible().requireEnabled().click(); 
     // write our own matcher 
     JTextComponentFixture centerArea = this.frame.textBox(new GenericTypeMatcher(JTextArea.class, true) { 
      @Override 
      protected boolean isMatching(Component component) { 
       return true; 
      } 
     }); 
     centerArea.requireVisible().requireEnabled().requireText("North, South, "); 
    } 

    @Override 
    protected void onTearDown() { 
     this.northButtonFixture = null; 
     this.southButtonFixture = null; 
    } 
} 

一旦你在你的项目中这样的基本设置,你可能W¯¯蚂蚁可以调查各种组件匹配器,并且可能会为您想要测试的各种组件调用几个setName()调用,以便让您的生活更加轻松。

+0

在setUp()中,我们将如何获取第三方Java Web Applet,它是已经运行? –

+0

@StevenVascellaro也许看看AssertJ Swing网站上的描述:http://joel-costigliola.github.io/assertj/assertj-swing-launch.html – Carsten