 
 
     
    |   | 
 | 
|   | 
 | 
|   | 
 | 
|   | 
 | 
|   | 
 | 
|   | 
 | 
 
     
      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 |