Next big feature - Automate your GUI Tests
It's not a problem to automate GUI tests, nowadays. There are different approaches and tools. A nice tool overview is available on wikipedia.
The tools have different techniques to perform tests. On technique is sending/simulating mouse clicks to components. The problem is that the GUI shouldn't be changed between tests and you should use a special test environment/system. Another technique is tricky but there's a nice implementation from MIT. It works with image recognition. And finally there's a solution based on unique component identifiers.
We like the last technique, based on unique component identifiers because it allows changing the UI without changing test cases - we love flexibility. The only problem with JVx was that we didn't have unique identifiers and a developer won't set an identifier for every component. We didn't have a mechanism to create unique identifiers... BUT now... we have.
With upcoming JVx releases, it will be possible to test GUIs automatically based on unique identifiers. This identifiers will be available also for VaadinUI. Another advantage for VaadinUIs will be that the id could be used for styling via CSS.
There are still some todos for us but we're happy to be able to show you some examples.
We made some test-cases with different test tools. To test our VaadinUI, we use Selenium together with PhantomJS.
The source code might look like this one:
{
private static final int TIMEOUT_IN_SECONDS = 10;
private static final String TIMEOUT_IN_MILLISECONDS_AS_STRING =
Integer.toString(TIMEOUT_IN_SECONDS * 1000);
private WebDriver driver;
private Selenium selenium;
@Before
public void setUp()
{
driver = configureDriver(createPhantomJsDriver());
String baseUrl = "http://localhost:8080/";
selenium = new WebDriverBackedSelenium(driver, baseUrl);
selenium.setTimeout(TIMEOUT_IN_MILLISECONDS_AS_STRING);
}
@Test
public void testDemoerptest()
{
selenium.open("/VisionX.Server/app/web/ui/DemoERP/");
waitForPage();
selenium.type("id=UserName", "admin");
selenium.type("id=Password", "admin");
selenium.click("id=OK");
waitForPage();
selenium.click("id=DemoERP_P1_P1_P2_B1");
waitForPage();
selenium.click(
"//div[@id='NavigationTable']/div[2]/div/table/tbody/tr[2]/td[2]/div");
selenium.click(
"//div[@id='NavigationTable']/div[2]/div/table/tbody/tr[3]/td[2]/div");
selenium.click(
"//div[@id='NavigationTable']/div[2]/div/table/tbody/tr[2]/td[2]/div");
selenium.click(
"//div[@id='NavigationTable']/div[2]/div/table/tbody/tr/td[2]/div");
selenium.click("id=DemoERP_P1_P1_P2_B2");
waitForPage();
selenium.click(
"//div[@id='NavigationTable']/div[2]/div/table/tbody/tr[2]/td/div");
selenium.click(
"//div[@id='NavigationTable']/div[2]/div/table/tbody/tr[3]/td/div");
selenium.click(
"//div[@id='NavigationTable']/div[2]/div/table/tbody/tr[4]/td/div");
selenium.click("id=DemoERP_P1_P1_P2_B3");
waitForPage();
selenium.click("xpath=(//button[@type='button'])[7]");
waitForPage();
}
@After
public void tearDown()
{
driver.quit();
selenium.stop();
}
private WebDriver configureDriver(WebDriver pWebDriver)
{
pWebDriver.manage().timeouts().implicitlyWait(TIMEOUT_IN_SECONDS,
TimeUnit.SECONDS);
pWebDriver.manage().timeouts().pageLoadTimeout(TIMEOUT_IN_SECONDS,
TimeUnit.SECONDS);
pWebDriver.manage().timeouts().setScriptTimeout(TIMEOUT_IN_SECONDS,
TimeUnit.SECONDS);
pWebDriver.manage().window().setSize(new Dimension(1920, 1080));
return pWebDriver;
}
@SuppressWarnings("unused")
private WebDriver createChromeDriver()
{
ChromeOptions options = new ChromeOptions();
options.addArguments("-incognito");
return new ChromeDriver(options);
}
private WebDriver createPhantomJsDriver()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(
PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
"D:\\opt\\phantomjs\\phantomjs.exe");
return new PhantomJSDriver(capabilities);
}
private void waitForPage()
{
selenium.waitForPageToLoad(TIMEOUT_IN_MILLISECONDS_AS_STRING);
}
}
We use FEST (doc) for Swing UIs. The library is old (like Swing) but still works!
A very simple example with FEST:
{
private FrameFixture window;
@After
public void cleanItUp()
{
super.cleanUp();
}
@Before
public void setUp()
{
super.setUpRobot();
UIFactoryManager.getFactoryInstance(SwingFactory.class);
WorkScreenCreatingGuiQuery query = new WorkScreenCreatingGuiQuery();
Frame frame = GuiActionRunner.execute(query);
window = new FrameFixture(robot(), frame);
window.show();
query.afterShow();
}
@Test
public void testTheUIStuff()
{
// Check if the editor does have the correct value.
// The WorkScreen does select the first line for us.
Assert.assertEquals("John Smith",
window.textBox("UITWS_E_TESTDATABOOK_NAME").text());
// Select the next line in the table.
window.table("UITWS_T_TESTDATABOOK").selectRows(1);
Assert.assertEquals("Ian McIrish",
window.textBox("UITWS_E_TESTDATABOOK_NAME").text());
// Select the tab that has that TextField-Button-Label combo on it.
window.tabbedPane("UITWS_TP1").selectTab(1);
window.tabbedPane("UITWS_TP1_P2_TP1").selectTab(2);
// Enter something into the textfield, press the button and the label
// should now have the same text.
window.textBox("UITWS_TP1_P2_TP1_P3_P1_TF1").setText("This is an automated test.");
window.button("UITWS_TP1_P2_TP1_P3_P1_B1").click();
Assert.assertEquals("This is an automated test.",
window.label("UITWS_TP1_P2_TP1_P3_P1_L2").text());
}
private static final class WorkScreenCreatingGuiQuery extends GuiQuery<Frame>
{
private UIFrame frame;
private WorkScreen workscreen;
void afterShow()
{
// Important so that everything gets a name.
// There is a window constructed, but outside of the JVx scope.
frame.addNotify();
workscreen.notifyRepaint();
}
@Override
protected Frame executeInEDT() throws Throwable
{
frame = new UIFrame();
workscreen = new UITestingWorkScreen();
frame.add(workscreen);
return (Frame) frame.getResource();
}
private String dumpStructure()
{
return dumpStructure(frame, 1);
}
}
}