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;
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我知道这个问题本身非常广泛,因此我自己提供了一个答案 - 展示这个特定的例子。
假设这是一个 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>\nRun Code Online (Sandbox Code Playgroud)\n\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}\nRun Code Online (Sandbox Code Playgroud)\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}\nRun Code Online (Sandbox Code Playgroud)\n\n一旦您在项目中进行了这样的基本设置,您可能需要研究各种类型的组件匹配器,并可能对setName()您想要测试的各种组件引入一些调用,以使您的生活变得更轻松。
| 归档时间: |
|
| 查看次数: |
7357 次 |
| 最近记录: |