Reusing Functional Tests – Part 1
While writing some tests for SWTBot, there was quite some code duplication that could be removed by using some simple test patterns. This is part of a series of articles about how test code could be written better. This example uses SWTBot as a driver to drive an Eclipse application used by ACME Corporation.
Here is a snippet of a Test Case that creates an account in ACME Corporation’s customer management system.
public void testCreatesAccount() throws Exception {
bot.menu("Accounts").menu("New").click();
// nice wizard
bot.shell("Create a new account").activate();
bot.textWithLabel("Name:").setText("Will Coyote");
bot.textWithLabel("Email:").setText("will@coyote");
bot.button("Next>").click();
// yet another wizard
bot.shell("Address Details").activate();
bot.textWithLabel("Mailing Address:").setText("Will Coyote, middle of, nowhere");
bot.button("Finish").click();
assertThatAccountIsCreated("Will Cooyte", "will@coyote.com");
}
That’s a great start to begin writing tests. But there’s the problem: What happens if the test fails somewhere and the “Create a new account” or the “Address Details” window remains open ? This means that the rest of the tests fail or do not run at all. Not a problem, tearDown() to the rescue:
protected void tearDown() throws Exception {
closeWindow("Create a new account");
closeWindow("Address Details");
}
private void closeWindow(String windowTitle) throws Exception {
try {
bot.shell(windowTitle).close();
} catch (WidgetNotFoundException e) {
// do nothing if the window is not found
}
}
Now we write a test that tests if you’re able to create two users with the same name.
public void testShouldNotCreateTwoAccountsWithTheSameName() throws Exception {
bot.menu("Accounts").menu("New").click();
// nice wizard
bot.shell("Create a new account").activate();
bot.textWithLabel("Name:").setText("Will Coyote");
bot.textWithLabel("Email:").setText("will@coyote");
bot.button("Next>").click();
// yet another wizard
bot.shell("Address Details").activate();
bot.textWithLabel("Mailing Address:").setText("Will Coyote, middle of, nowhere");
bot.button("Finish").click();
assertThatAccountIsCreated("Will Cooyte", "will@coyote.com");
bot.menu("Accounts").menu("New").click();
// nice wizard
bot.shell("Create a new account").activate();
bot.textWithLabel("Name:").setText("Will Coyote");
bot.textWithLabel("Email:").setText("will@coyote");
bot.button("Next>").click();
// yet another wizard
bot.shell("Address Details").activate();
bot.textWithLabel("Mailing Address:").setText("Will Coyote, middle of, nowhere");
bot.button("Finish").click();
assertThatAccountIsNotCreated("Will Cooyte", "will@coyote.com");
}
Duplication, not a big deal, some bit of refactoring and you get to this. This also reduces the number of places where you’ll need to fix the test, if the label on one of the screens change.
public void testCreatesAccount() throws Exception {
createAccount("Will Coyote", "will@coyote", "Will Coyote, middle of, nowhere");
assertThatAccountIsCreated("Will Cooyte", "will@coyote.com");
}
public void testShouldNotCreateTwoAccountsWithTheSameName() throws Exception {
createAccount("Will Coyote", "will@coyote", "Will Coyote, middle of, nowhere");
assertThatAccountIsCreated("Will Cooyte", "will@coyote.com");
createAccount("Will Coyote", "will@coyote", "Will Coyote, middle of, nowhere");
assertThatAccountIsNotCreated("Will Cooyte", "will@coyote.com");
}
private void createAccount(String name, String email, String snailMail) throws WidgetNotFoundException, TimeoutException {
bot.menu("Accounts").menu("New").click();
// nice wizard
bot.shell("Create a new account").activate();
bot.textWithLabel("Name:").setText(name);
bot.textWithLabel("Email:").setText(email);
bot.button("Next>").click();
// yet another wizard
bot.shell("Address Details").activate();
bot.textWithLabel("Mailing Address:").setText(snailMail);
bot.button("Finish").click();
}
In the next article, you’ll get to see how you can reuse code better by using Screen Objects
Hi! I want to know about your idea of using screen objects. When are you going to write part 2?
Leo Arias
4 Sep 08 at 5:34 am
@Leo, hold on for a couple of days. I’m already writing this one and it’s still a draft.
Ketan
6 Sep 08 at 8:50 am
cool, I’ll be waiting.
Leo Arias
12 Sep 08 at 9:06 pm
Is Screen Object the same concept as a Page Object?
http://code.google.com/p/webdriver/wiki/PageObjects
Jason Yip
15 Oct 08 at 2:58 pm
@Jason,
Yes, that is the same concept — Providing classes that expose operations/services that users can perform on a page rather than the raw UI controls.
Ketan
15 Oct 08 at 5:47 pm
[...] SWTBot also provides a recorder, what’s its current state? The recorder was developed as a proof of concept, and has not been touched much. It lacks support for a lot of controls, and does not record all operations, although it is quite trivial to add support for these. The recorder records code in an intermediate format, similar to an Abstract Syntax Tree. This representation can then be output in a language of your choice, currently it supports Java, but even Ruby is possible. On another note: Record and playback is not the recommended way to write tests (see TestingGUIApplications and Recording vs. Coding). Primarily because the complete script for a workflow (or all the scripts) need to be recorded again for tiny changes. SWTBot recommends using the PageObject/ScreenObject pattern (or see reusing functional tests). [...]
Grass’s Blog - Asp.net|C# » Blog Archive » Java GUI Testing With JRuby
29 Oct 08 at 7:37 pm
Great Article.
When comes Part 2 about the using of screen objects?
Rob Meier
17 Aug 09 at 2:45 pm