当前位置: 首页 > 知识库问答 >
问题:

如何入门:使用AssertJ Swing测试Java Swing GUI

梁渊
2023-03-14

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

这个答案(关于“基于Swing的应用程序的最佳测试工具是什么?”)建议使用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;
    }
}

我意识到这个问题本身非常广泛,因此我自己提供了一个答案--展示--举例说明这个特定的例子。

共有1个答案

洪飞驰
2023-03-14

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

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

  1. 单元测试框架(例如这里的JUnit-但也可以使用Testng)
  2. 匹配的AssertJ swing库(例如在这里AssertJ-swing-junit)
<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;
    }
}

一旦在项目中有了这样的基本设置,您可能需要研究现有的各种组件匹配器,并可能在您要测试的各种组件上引入几个setname()调用,以便使您的生活更加轻松。

 类似资料:
  • 1.1. 什么是A/B测试? A/B 测试,简单来说,就是为同一个目标制定两个方案(比如两个页面),让一部分用户使用 A 方案,另一部分用户使用 B 方案,记录下用户的使用情况,根据不同方案的结果数据(例如:下单)来确定方案的优劣。AB测试如下图设计: 1.2. A/B测试有哪些场景可以使用? 市场同事不同的落地页设计以及推广方案设计 运营活动测试不同运营流程或者文案测试 策划,交互,视觉等产品用

  • 简介 Laravel 天生就具有测试的基因。事实上,Laravel 默认就支持用 PHPUnit 来做测试,并为你的应用程序配置好了 phpunit.xml 文件。框架还提供了一些便利的辅助函数,让你可以更直观的测试应用程序。 默认在你应用的 tests 目录下包含了两个子目录: Feature 和 Unit。单元测试是针对你代码中相对独立而且非常少的一部分代码来进行测试。实际上,大多数单元测试可

  • 原文地址:http://truffleframework.com/docs/getting_started/testing 框架 Truffle使用Mocha测试框架来做自动化测试,使用Chai来做断言。这两个库的结合可能让人耳目一新,我们基于这两者之上,提供一种方式来编译简单和可管理的合约自动化测试用例。 位置 测试文件应置于./tests目录。Truffle只会运行以.js,.es,.es6和

  • 问题内容: 我有一个用Python编写的控制台程序。它使用以下命令询问用户问题: 如何测试包含对using的调用的函数?我不想强迫测试人员多次输入文本只是为了完成一次测试运行。 问题答案: 您可能应该模拟内置功能,可以在每次测试后使用提供的功能还原为原始功能。 更好的解决方案是将模块与一起使用。这样,您就不需要使用拆解,并且修补的方法只会存在于范围内。

  • 问题内容: 我正在使用JavaScript测试运行程序“摩卡”。 我的测试失败了,因此我将使用进行调试。 但是运行测试时,没有输出(仅来自Mocha的测试结果)。看来Mocha已捕获并抑制了我的输出! 如何让Mocha显示输出?(对于失败的测试)? 编辑: 抱歉!- 在测试期间可以正常工作!我肯定一直期望它抑制输出,而且我没有正确检查自己的代码。感谢您的回应。所以…话虽如此…也许抑制通过测试的输出

  • 问题内容: 我想对用Apache CXF编写的RESTful接口进行单元测试。 我使用ServletContext来加载一些资源,所以我有: 如果我将其部署在Glassfish上,则会注入ServletContext,并且它会按预期工作。但是我不知道如何将ServletContext注入我的服务类中,以便可以使用JUnit测试对其进行测试。 我使用Spring 3.0,JUnit 4,CXF 2.