Car*_*ten 5 java swing ui-testing assertj
在使用 Swing 开发 Java 桌面应用程序时,我遇到了直接测试 UI 的需要,而不仅仅是通过单元测试测试底层控制器/模型类。
这个答案(关于“基于 Swing 的应用程序的最佳测试工具是什么?”)建议使用FEST,不幸的是,它已停止使用。然而,有一些项目是从 FEST 离开的地方继续进行的。特别是一个(在这个答案中提到)引起了我的注意,因为我之前在单元测试中使用过它: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;
    }
}
我知道这个问题本身非常广泛,因此我自己提供了一个答案 - 展示这个特定的例子。
假设这是一个 Maven 项目,您首先需要添加至少两个依赖项:
\n\njunit\xe2\x80\x93 但也可以使用testng)AssertJ Swing库(例如这里assertj-swing-junit)它可能看起来像这样(在你的pom.xml:
<dependency>\n    <groupId>junit</groupId>\n    <artifactId>junit</artifactId>\n    <version>4.12</version>\n    <scope>test</scope>\n</dependency>\n<dependency>\n    <groupId>org.assertj</groupId>\n    <artifactId>assertj-swing-junit</artifactId>\n    <version>1.2.0</version>\n    <scope>test</scope>\n</dependency>\n其次,我通常选择一个基本测试类来将大部分测试设置与实际测试分开:
\n\n/**\n * Base class for all my UI tests taking care of the basic setup.\n */\npublic class AbstractUiTest extends AssertJSwingTestCaseTemplate {\n\n    /**\n     * The main entry point for any tests: the wrapped MainWindow.\n     */\n    protected FrameFixture frame;\n\n    /**\n     * Installs a {@link FailOnThreadViolationRepaintManager} to catch violations of Swing threading rules.\n     */\n    @BeforeClass\n    public static final void setUpOnce() {\n        // avoid UI test execution in a headless environment (e.g. when building in CI environment like Jenkins or TravisCI)\n        Assume.assumeFalse("Automated UI Test cannot be executed in headless environment", GraphicsEnvironment.isHeadless());\n        FailOnThreadViolationRepaintManager.install();\n    }\n\n    /**\n     * Sets up this test\'s fixture, starting from creation of a new <code>{@link Robot}</code>.\n     *\n     * @see #setUpRobot()\n     * @see #onSetUp()\n     */\n    @Before\n    public final void setUp() {\n        // call provided AssertJSwingTestCaseTemplate.setUpRobot()\n        this.setUpRobot();\n        // initialize the graphical user interface\n        MainWindow mainWindow = GuiActionRunner.execute(new GuiQuery<MainWindow>() {\n\n            @Override\n            protected MainWindow executeInEDT() throws Exception {\n                return MainApp.showWindow();\n            }\n        });\n        this.frame = new FrameFixture(this.robot(), mainWindow);\n        this.frame.show();\n        this.frame.resizeTo(new Dimension(600, 600));\n        onSetUp();\n    }\n\n    /**\n     * Subclasses that need to set up their own test fixtures in this method. Called as <strong>last action</strong> during {@link #setUp()}.\n     */\n    protected void onSetUp() {\n        // default: everything is already set up\n    }\n\n    /*****************************************************************************************\n     * Here you could insert further helper methods, e.g. frequently used component matchers *\n     *****************************************************************************************/\n\n    /**\n     * Cleans up any resources used in this test. After calling <code>{@link #onTearDown()}</code>, this method cleans up resources used by this\n     * test\'s <code>{@link Robot}</code>.\n     *\n     * @see #cleanUp()\n     * @see #onTearDown()\n     */\n    @After\n    public final void tearDown() {\n        try {\n            onTearDown();\n            this.frame = null;\n        } finally {\n            cleanUp();\n        }\n    }\n\n    /**\n     * Subclasses that need to clean up resources can do so in this method. Called as <strong>first action</strong> during {@link #tearDown()}.\n     */\n    protected void onTearDown() {\n        // default: nothing more to tear down\n    }\n}\n实际的测试类可能如下所示:
\n\npublic class MainWindowTest extends AbstractUiTest {\n\n    private JButtonFixture northButtonFixture;\n    private JButtonFixture southButtonFixture;\n\n    @Override\n    protected void onSetUp() {\n        this.northButtonFixture = this.frame.button(JButtonMatcher.withText("North"));\n        this.southButtonFixture = this.frame.button(JButtonMatcher.withText("South"));\n    }\n\n    @Test\n    public void testWithDifferingComponentMatchers() {\n        // use JTextComponentMatcher.any() as there is only one text input\n        this.frame.textBox(JTextComponentMatcher.any()).requireVisible().requireEnabled().requireNotEditable().requireEmpty();\n        this.northButtonFixture.requireVisible().requireEnabled().click();\n        // use value assigned in MainWindow class via JTextArea.setName("Center-Area") to identify component here\n        this.frame.textBox("Center-Area").requireText("North, ");\n\n        this.southButtonFixture.requireVisible().requireEnabled().click();\n        // write our own matcher\n        JTextComponentFixture centerArea = this.frame.textBox(new GenericTypeMatcher(JTextArea.class, true) {\n            @Override\n            protected boolean isMatching(Component component) {\n                return true;\n            }\n        });\n        centerArea.requireVisible().requireEnabled().requireText("North, South, ");\n    }\n\n    @Override\n    protected void onTearDown() {\n        this.northButtonFixture = null;\n        this.southButtonFixture = null;\n    }\n}\n一旦您在项目中进行了这样的基本设置,您可能需要研究各种类型的组件匹配器,并可能对setName()您想要测试的各种组件引入一些调用,以使您的生活变得更轻松。
| 归档时间: | 
 | 
| 查看次数: | 7357 次 | 
| 最近记录: |