|
|
|
|
|
|
|
|
|
|
|
A well understood problem
public class Greeter {
private PhraseBuilder phraseBuilder;
@Inject
public Greeter(PhraseBuilder phraseBuilder) {
this.phraseBuilder = phraseBuilder;
}
public void greet(PrintStream to, String name) {
to.println(createGreeting(name));
}
public String createGreeting(String name) {
return phraseBuilder.buildPhrase("hello", name);
}
}
public class PhraseBuilder {
private Map<String, String> templates;
public String buildPhrase(String id, Object... args) {
return MessageFormat.format(templates.get(id), args);
}
@PostConstruct
public void initialize() {
templates = new HashMap<String, String>();
templates.put("hello", "Hello, {0}!");
}
}
@RunWith(Arquillian.class)
public class GreeterTest {
@Deployment
public static JavaArchive createDeployment() {
JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
.addClasses(Greeter.class, PhraseBuilder.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
return jar;
}
@Inject
Greeter greeter;
@Test
public void should_create_greeting() {
Assert.assertEquals("Hello, Earthling!",
greeter.createGreeting("Earthling"));
}
}
<profile>
<id>arquillian-jbossas-managed</id>
<dependencies>
...
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-arquillian-container-managed</artifactId>
<version>7.1.1.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.protocol</groupId>
<artifactId>arquillian-protocol-servlet</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
WebDriver API:
WebDriver
Represents the web browser
WebElement
Represents an HTML element.
Selenium/WebDriver programmatic API:
WebElement username
= driver.findElement(By.id("username"))
Alternative @FindBy annotation:
@FindBy(id="username")
WebElement username;
...
PageFactory.initElements(driver, this);
public class SeleniumTest {
private WebDriver driver;
private String baseUrl;
private StringBuffer verificationErrors = new StringBuffer();
@Before
public void setUp() throws Exception {
driver = new FirefoxDriver();
baseUrl = "http://kitchensink-richfaces.rhcloud.com/";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
@Test
public void test() throws Exception {
driver.get(baseUrl + "/");
driver.findElement(By.id("reg:memberForm:name")).clear();
driver.findElement(By.id("reg:memberForm:name")).sendKeys("Brian Leathem");
driver.findElement(By.id("reg:memberForm:email")).clear();
driver.findElement(By.id("reg:memberForm:email")).sendKeys("[email protected]");
driver.findElement(By.id("reg:memberForm:phoneNumber")).clear();
driver.findElement(By.id("reg:memberForm:phoneNumber")).sendKeys("234234234235");
driver.findElement(By.id("reg:register")).click();
Thread.sleep(2000);
try {
assertEquals("Brian Leathem", driver
.findElement(By.id("reg:memberTable:0:member_name")).getText());
} catch (Error e) {
verificationErrors.append(e.toString());
}
}
@After
public void tearDown() throws Exception {
driver.quit();
String verificationErrorString = verificationErrors.toString();
if (!"".equals(verificationErrorString)) {
fail(verificationErrorString);
}
}
}
Not enough abstraction, Highly repetitive
→ Maintenance problem
Manages the browser life-cycle
@Drone
WebDriver browser
<extension qualifier="webdriver">
<property name="browserCapabilities">chrome</property>
</extension>
Devs focus: Author tests
QA focus: Automate tests
@RunWith(Arquillian.class)
public class GoogleDroneTest {
@Drone
WebDriver browser;
@Test
public void testOpeningHomePage() {
browser.get("http://www.google.com/");
List<WebElement> elements = browser.findElements(
By.xpath("//span[contains(text(), 'Google Search')]"));
Assert.assertTrue("Page not loaded", elements.size() > 0);
}
}
Selenium Arquillian-Style
@Drone
GrapheneSelenium browser;
Encapsulate DOM elements behind a custom API
public class LoginPage {
private final WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public HomePage loginAs(String username, String password) {
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("passwd")).sendKeys(password);
driver.findElement(By.id("login")).submit();
return new HomePage(driver);
}
}
Selenium PageFactory class instantiates Page Objects:
LoginPage loginPage =
PageFactory.initElements(driver, LoginPage.class);
Graphene test enrichment manages the PageFactory
@Page
LoginPage loginPage
@FindBy(id="username")
WebElement username;
@FindBy(css=".calendar")
CalendarFragment calendar;
Single place to update WebDriver code
when the underlying DOM changes
Simplest case:
Thread.sleep(2000);
Selenium provides the WebDriverWait class
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(new ExpectedCondition<WebElement>(){
@Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id("myDynamicElement"));
}});
More boiler plate!
Helper | Description | Timeout |
---|---|---|
waitGui() |
waits for a short time
eg. wait for client-side operations |
1 |
waitAjax() |
waits for longer time
eg. wait for simple ajax request |
2 |
waitModel() |
waits for a long time
eg. wait for database requests |
5 |
By id = By.id("button");
WebElement element = driver.findElement(id);
...
waitModel(element(id).isVisible));
...
waitModel(element(element).not().textContains("blahblah"));
...
waitModel(attribute(id, "value").valueContains("blahblah"));
...
waitModel(attribute(element, "value").not().valueEquals("blahblah"));
Blocks the Selenium test execution until a network communication caused by a given action ends
@RunWih(Arquillian.class)
public class TestClass {
@FindBy(id="http")
private WebElement httpButton;
@FindBy(id="xhr")
private WebElement xhrButton;
@Test
public void testSimple() {
guardHttp(httpButton).click();
guardXhr(xhrButton).click();
}
}
Execute JavaScript from your Java test
@JavaScript("javascriptObject")
@Dependency(sources = {"file.js"})
public interface Background {
void voidMethod(String color);
String someFunction();
}
BackGround.java:
@JavaScript("myBackground")
@Dependency(sources = {"background.js"})
public interface Background {
void setBackground(String color);
String getBackground();
}
background.js:
myBackground = {
setBackground : function (color) {
document.body.style.background = color;
},
getBackground : function () {
return document.body.style.background;
}
}
@RunWith(Arquillian.class)
public class JavaScriptTest {
@Drone
WebDriver browser;
@Test
public void testOpeningHomePage() throws Exception {
browser.get("http://www.google.com/");
Background background = JSInterfaceFactory.create(Background.class);
System.out.println(String.format(
"Background color is: %s", background.getBackground()));
background.setBackground("red");
System.out.println(String.format(
"Background color is: %s", background.getBackground()));
Thread.sleep((2000));
background.setBackground("");
System.out.println(String.format(
"Background color is: %s", background.getBackground()));
Thread.sleep((2000));
}
}
Automatically include Javascript Interfaces in your tests
Simply:
See the docs...
https://docs.jboss.org/author/display/ARQGRA2/Page+Extensions@JavaScript(value = "Graphene.Page.RequestGuard")
@Dependency(sources = "Graphene.Page.RequestGuard.js",
interfaces=XhrInterception.class)
public interface RequestGuard extends InstallableJavaScript {
/**
* @return the last request type
*/
RequestType getRequestDone();
/**
* Clears the request type cache and returns the last
* request type
* @return the last request type
*/
RequestType clearRequestDone();
}
Work is in progress to port the Graphene 1 interceptor API to Graphene 2
public class Deployments {
public static final String WEBAPP_SRC = "src/main/webapp";
public static WebArchive getLoginScreenDeployment() {
return ShrinkWrap.create(WebArchive.class, "login.war")
.addClasses(Credentials.class, User.class, LoginController.class)
.addAsWebResource(new File(WEBAPP_SRC, "resources/bootstrap/css/bootstrap.css"),
"resources/bootstrap/css/bootstrap.css")
.addAsWebResource(new File(WEBAPP_SRC, "login.xhtml"))
.addAsWebResource(new File(WEBAPP_SRC, "home.xhtml"))
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsWebInfResource(
new StringAsset("<faces-config version=\"2.0\"/>"),
"faces-config.xml");
}
}
@RunWith(Arquillian.class)
public class LoginScreenGrapheneTest {
@Deployment(testable = false)
public static WebArchive createDeployment() {
return Deployments.getLoginScreenDeployment();
}
@Drone
WebDriver browser;
@ArquillianResource
URL contextPath;
@FindBy(id="loginForm:username")
private WebElement usernameInput;
@FindBy(id="loginForm:password")
private WebElement passwordInput;
@FindBy(id="loginForm:login")
private WebElement loginButton;
@Test
@RunAsClient
public void should_login_successfully() throws Exception {
String page = contextPath + "login.jsf";
browser.get(page);
usernameInput.sendKeys("demo");
passwordInput.sendKeys("demo");
loginButton.click();
Assert.assertTrue("User should be logged in!",
browser.findElements(By.xpath("//li[contains(text(), 'Welcome')]")).size() > 0);
}
}
public class LoginFragment {
@FindBy(id="loginForm:username")
private WebElement usernameInput;
@FindBy(id="loginForm:password")
private WebElement passwordInput;
@FindBy(id="loginForm:login")
private WebElement loginButton;
public void setUsername(Object username) {
usernameInput.sendKeys(username.toString());
}
public void setPassword(Object password) {
passwordInput.sendKeys(password.toString());
}
public void click() {
loginButton.click();
}
}
@RunWith(Arquillian.class)
public class LoginScreenFragmentTest {
@Deployment(testable = false)
public static WebArchive createDeployment() {
return Deployments.getLoginScreenDeployment();
}
@Drone
WebDriver browser;
@ArquillianResource
URL contextPath;
@FindBy(id="loginForm")
LoginFragment loginForm;
@Test
@RunAsClient
public void should_login_successfully() {
String page = contextPath + "login.jsf";
browser.get(page);
loginForm.setUsername("demo");
loginForm.setPassword("demo");
loginForm.click();
Assert.assertTrue("User should be logged in!",
browser.findElements(By.xpath("//li[contains(text(), 'Welcome')]")).size() > 0);
}
}
@WarpTest
@RunWith(Arquillian.class)
public class LoginScreenWarpTest {
@Deployment
public static WebArchive createDeployment() {
WebArchive webArchive = Deployments.getLoginScreenDeployment();
webArchive.delete("WEB-INF/beans.xml");
webArchive.addAsWebInfResource(new File("src/test/resources/beans.xml"));
return webArchive;
}
@Drone
WebDriver browser;
@ArquillianResource
URL contextPath;
@FindBy(id="loginForm")
LoginFragment loginForm;
@Test
@RunAsClient
public void should_login_successfully() {
String page = contextPath + "login.jsf";
browser.get(page);
Warp.filter(new JsfRequestFilter()).execute(new ClientAction() {
@Override
public void action() {
loginForm.setUsername("demo");
loginForm.setPassword("demo");
loginForm.click();
}
}).verify(new CheckUsername());
Assert.assertTrue("User should be logged in!",
browser.findElements(By.xpath("//li[contains(text(), 'Welcome')]"))
.size() > 0);
}
public static class CheckUsername extends ServerAssertion {
@Inject
Credentials credentials;
@BeforePhase(Phase.UPDATE_MODEL_VALUES)
public void beforeUpdateModelValues() {
Assert.assertNull(credentials.getUsername());
}
@AfterPhase(Phase.UPDATE_MODEL_VALUES)
public void afterUpdateModelValues() {
Assert.assertEquals("demo", credentials.getUsername());
}
}
}
Best practices
Project | arquillian.org | richfaces.org |
Twitter: | @arquillian | @richfaces |
Google+: | +Arquillian | +RichFaces |
Forums: | Arquillian User forum | RichFaces User forum |
IRC | #jbosstesting | #richfaces |
Blog feed: | arquillian.org/blog/ |
planet.jboss.org /feed/richfacesall |