Monday, January 21, 2013

horizontally refactor selenium example using page (100 mcg)

When people talk about Refactoring, most of time, it is about Vertical Refactoring as laid out in the Refactoring book by Martin Fowler. Actually, there is another kind of refactoring, Horizontal Refactoring, which is quite useful in writing more comprehensible code.

Please take a look of the example in this page,

http://code.google.com/p/selenium/wiki/PageObjects Do you see duplicated code?
// ☹ this is a bad example, please don't follow the style.
public class LoginPage {
    private final WebDriver driver;

    public LoginPage(WebDriver driver) {
        this.driver = driver;

        // Check that we're on the right page.
        if (!"Login".equals(driver.getTitle())) {
            // Alternatively, we could navigate to the login page, perhaps logging out first
            throw new IllegalStateException("This is not the login page");
        }
    }

    // Conceptually, the login page offers the user the service of being able to "log into"
    // the application using a user name and password. 
    public HomePage loginAs(String username, String password) {
        // This is the only place in the test code that "knows" how to enter these details
        driver.findElement(By.id("username")).sendKeys(username);
        driver.findElement(By.id("passwd")).sendKeys(password);
        driver.findElement(By.id("login")).submit();
         
        // Return a new page object representing the destination. Should the login page ever
        // go somewhere else (for example, a legal disclaimer) then changing the method signature
        // for this method will mean that all tests that rely on this behaviour won't compile.
        return new HomePage(driver);
    }
}
Most people would tolerate this kind of duplicates and so far no tool can detect this kind of duplicates either. However, this coding style cluttered source code with too much information and slows down people's speed since there are too much code to read,

Here is refactored version of the same code, you would see much cleaner code base. I recommend using this style, achieved through Horizontal Refactoring.
//   ☺ This is an good example.
public class LoginPage extends AbstractPage{

    public HomePage loginAs(String username, String password) {
        put(USERNAME, username);
        put(PASSWORD, password);
        button(LOGIN).click();
        return new HomePage(this);
    }
}

These are the methods handling text input field,
 //   ☺ This is an good example.

    /**
     * Enter text into an input filed.
     *
     * @param method
     * @param value
     */
    default public void put(Supplier<By> method, Object value) {
        new Input<>((Where) this).put(method, value);
    }

    /**
     * Autocomplete for text field and return the first found suggestion match the whole word.
     *
     * @param method
     * @param value
     * @param locator
     */
    default public void autocomplete(Supplier<By> method, Object value, Locator<Where, Element> locator) {
        new Input<>((Where) this).autocomplete(method, value, locator);
    }

New code is much cleaner.

Tuesday, October 23, 2012

essential tools for writing effective selenium tests (100 mcg)

When writing Selenium tests, Selenium API alone is not enough. There are some tools which are essential in functional testing.

1. Firefox Browser

Selenium has implemented most popular browsers such as Internet Explorer, Safari, Opera, iPhone, Android and Firefox. In a project, due to cost constraint, usually it is very difficult to writing tests for many browsers, I choose to use Firefox driver since it comes with a Screenshot saving function to allow recording of screenshot at anytime. I choose to take a screenshot before and after clicking a WebElement which invoke a HTTP request so when when things go wrong, I can combine the log on build machine and the screenshot series to figure out the cause of the problems. Another reason I choose to use Firefox is the availability of Firebug.


2. Firebug

https://getfirebug.com/

Even you are not writing Selenium tests, as long as you are doing web development, Firebug should be included in your toolkit. I use Firebug to inspect the web pages and design a searching strategy for the WebElements. And for none significant information, just right click the element and select "Copy XPath" from the popup menu, and the information is ready to use,



WebDriver webDriver = new FirefoxDriver();
webDriver.findElements(By.xpath("/html/body/div/div/div[3]");

This query will return the WebElement for the bug. While these two lines of code may not be meaningful to most people, but if it is in a Page object, it becomes instantly meaningful. That's the reason we need to introduce a Page model into Selenium based function tests, see design of page (500 mcg) for nutrition information.


public class FirebugHomePage {

   WebDriver webDriver = new FirefoxDriver();

   public WebElement getLargeFirebugImage() {
      webDriver.findElement(By.xpath("/html/body/div/div/div[3]");
   }
}


Friday, October 19, 2012

clickable interface (300 mcg)

Clickable is a simple interface with only one method click. It is a common interface for Url, Link and Button classes.

public interface Clickable {

    void click();

}
public class Clickables<Where extends Searchable<Where>> implements Clickable {

    private static final Logger log = getLogger(Clickables.class);
    private final Where where;
    private final Locator<Where, Element> locator;

    public Clickables(Where where, Locator<Where, Element> locator) {
        this.locator = locator;
        this.where = where;
    }

    @Override
    public void click() {
        Element apply = locator.locate(where);
        log.info("clicking [" + apply + "]");
        where.save();
        apply.click();
        where.save();
    }

    @Override
    public String toString() {
        return locator.toString();
    }
}
public class Link<Where extends Searchable<Where>> extends Clickables<Where> {

    public Link(Where where, Locator<Where, Element> locator) {
        super(where, locator);
    }
}

    /**
     * Find the link using the selector.
     *
     * @param selector selector
     * @return the link using the selector.
     */
    @SuppressWarnings("unchecked")
    default public Clickable link(Supplier<By> selector) {
        return new Link<>((Where) this, element(selector));
    }

    public static <Where extends Searchable<Where>> Locators<Where, Element> element(Supplier<By> selector) {
        return new ElementLocator<>(selector);
    }
public class ElementLocator<Where extends Searchable<Where>>
        extends Locators<Where, Element> {

    public ElementLocator(Supplier<By> selector) {
        super((Where where) -> where.untilFound(selector));
    }
}

this example show how to use click(), and until()

        manningPage.link(Java).click();
        Element element = manningPage.until(new ElementLocator<>(ActiveMQ_In_Action));
        element.click();
        manningPage.until(IS_COPYRIGHTED);

What benefit do we get from this Link class? There are couple of benefits, one advantage is to add some logging and screenshot generating logic before and after the clicking event, those screenshots can be used for problem solving purpose in case test fails, we can use the generated screenshots as a analysis input to figure out what went wrong during last time. For example, finding an element will fail if there is a server error and the application return an error page instead of the expected page. Without the screenshot, we wouldn't be sure why we can't find the element.

So the click method can be rewritten like this,

Besides Link, Submit input and some button can also submit http request to refresh pages, so these can be encapsulated into Submit, Button classes.


After Java 8 came out, the wait logic can be further simplified into an interface with default methods.
public interface ExplcitWait<T> {

    default public <T1> T1 until(Function<T, T1> predicate) {
        return until(30, SECONDS, predicate);
    }

    default public void until(Predicate<T> predicate) {
        until(30, SECONDS, predicate);
    }

    default public <T1> T1 until(int duration, TimeUnit timeUnit, Function<T, T1> function) {
        return getFluentWait(duration, timeUnit).until((T it) -> function.apply(it));
    }

    default public void until(int duration, TimeUnit timeUnit, Predicate<T> predicate) {
        getFluentWait(duration, timeUnit).until((T it) -> predicate.test(it));
    }

    default public FluentWait<T> getFluentWait(int duration, TimeUnit timeUnit) {
        return new FluentWait<>((T) this).withTimeout(duration, timeUnit).pollingEvery(50, MILLISECONDS).ignoring(Exception.class);
    }
}

An useful application of Clickable interface is to collect all the menu items into a Stream of Clickable and for each item inside the stream, click them one by one to implement the menu testing,

Thursday, October 18, 2012

explicit wait interface (200 mcg)

In my opinion, Selenium WebDriver should have a callback mechanism similar to AJAX callback and invoke the callback when HTTP request is settled. This would greatly reduce the waiting period in error cases. When expecting something to appear, there is no indicator to tell whether the element hasn't appear yet, or it will never appear. For now, according to Selenium document, there are two kind of waiting mechanish available to use,

1. Implicit Waits An implicit wait is to tell WebDriver to poll the DOM for a certain amount of time when trying to find an element or elements if they are not immediately available. The default setting is 0. Once set, the implicit wait is set for the life of the WebDriver object instance.
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));


The disadvantage is after setting implicit wait, WebDriver will wait for all elements which are not found at once which will dramatically slow down the tests in the error condition, so Selenium provided an alternative, Explicit Waits,

2. Explicit Waits An explicit waits is code you define to wait for a certain condition to occur before proceeding further in the code. The worst case of this is Thread.sleep(), which sets the condition to an exact time period to wait. There are some convenience methods provided that help you write code that will wait only as long as required. WebDriverWait in combination with ExpectedCondition is one way this can be accomplished.

WebDriver driver = new FirefoxDriver();
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
  .until(ExpectedConditions.presenceOfElementLocated(By.id("myDynamicElement")));
3. FluentWait and WebDriverWait This WebDriverWait, extends FluentWait which implements an interface Wait, and give a timeout as the maximum patience.

At first sight, FluentWait has more complex API then WebDriverWait, WebDriverWait is a refined memeber since it is easier to use and meet your testing need thus I didn't spend time to study FluentWait. I only use WebDriverWait and I found out the places FluentWait used can be replaced by WebDriverWait to simplify the source code.

For example, I found an example using FluentWait here



    FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver)
            .withTimeout(timeoutSeconds, TimeUnit.SECONDS)
            .pollingEvery(500, TimeUnit.MILLISECONDS)
            .ignoring(NoSuchElementException.class);

    WebElement element = wait.until(new Function<WebDriver, WebElement>() {
        public WebElement apply(WebDriver webDriver) {
            return driver.findElement(locator);
        }
}

can be simplified as this, less code to read for other developers,

    WebDriverWait wait = new WebDriverWait(driver, timeoutSeconds)

    WebElement element = wait.until(new Function<WebDriver, WebElement>() {
        public WebElement apply(WebDriver webDriver) {
            return driver.findElement(locator);
        }

}


As you can see, if you add Explicit Wait to the code, the codebase will be cluttered with this waiting mechanism repeated over and over again.

To avoid repeating this, an interface ExplicitWait is introduced, so it can be selectively called. In the the Searchable interface, a method untilFound is designed to call this until method in the ExplicitWait interface.

public interface ExplicitWait<Where> {

    void save();

    default public <What> What until(Locator<Where, What> predicate) {
        return until(30, SECONDS, predicate);
    }

    default public void until(Predicate<Where> predicate) {
        until(30, SECONDS, predicate);
    }

    default public <What> What until(int duration, TimeUnit timeUnit, Locator<Where, What> locator) {
        try {
            return getFluentWait(duration, timeUnit).until((Where where) -> locator.apply(where));
        } catch (RuntimeException e) {
            save();
            throw e;
        }
    }

    default public void until(int duration, TimeUnit timeUnit, Predicate<Where> predicate) {
        try {
            getFluentWait(duration, timeUnit).until((Where where) -> predicate.test(where));
        } catch (RuntimeException e) {
            save();
            throw e;
        }
    }

    default public FluentWait<Where> getFluentWait(int duration, TimeUnit timeUnit) {
        return new FluentWait<>((Where) this).withTimeout(duration, timeUnit).pollingEvery(50, MILLISECONDS).ignoring(Exception.class);
    }
}


If you want to have a more generic waiting interface like the ExplicitWait in this example, FluentWait is a better candidate to use since it allows you to give a parameterized type as shown. In the development of Selenium Capsules, the reason FluentWait is chosen over WebDriverWait is that there are 3 classes in the framework which wrapper the original WebDriver, WebElement from Selenium. They are AbstractPage, AbstractForm and Element which implement Waitable interface.

In the code, <Where> is a type parameter, it can be an AbstractPage, an Element, or an AbstractForm. Not only T, E can be used as generic type parameter, Where, What or other meaningful words, as long as not conflicting with the Class names used in the containing class, can be used as type parameters.

This interface then can be used everywhere in the project, and have AbstractPage implement it. After any clicking event which triggers a page refresh, call the one of the until methods, either pass in a Predicate or a Locator.

public abstract class AbstractPage implements ExplicitWait<AbstractPage> {

    @Override
    public final Element untilFound(By by) {
        return until(15, SECONDS, (AbstractPage page) -> browser.findElement(by));
    }


This untilFound greatly reduced the duplicate of the testing code. Without this method, each individual developer needs to add a wait statement after each findElement method call. The untilFound method abstracts the complexity away so the page class is cleaner.

/**
 *
 * Copyright (c) 2012, Algocrafts, Inc. All rights reserved.
 * Apache License version 2.
 */
package com.algocrafts.page;
 
import com.algocrafts.selenium.core.WaitWithPatience;
import com.google.common.base.Predicate;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class YujunsBlogPage extends AbstractPage {
 
   public void clickDesignOfDatePickerLink() {
      untilFound(By.linkText("Design of DatePicker").click();
   }
 
   public void clickDesignOfPageLink() {
      untilFound(By.linkText("Design of Page").click();
   }
}

One particular method, untilFound, will be waiting for the element to appear or throwing NoSuchElementException after timed out.

    @Override
    public final Element findElement(By by) {
        try {
            return browser.findElement(by);
        } catch (Exception e) {
        }
        return null;
    }


    /**
     * Find the first element or throw NoSuchElementException
     *
     * @param by selector
     * @return the first element or throw NoSuchElementException
     */
    @Override
    public final Element untilFound(final By by) {
        return until((AbstractPage page) -> browser.findElement(by));
    }

    @Override
    public final Stream<Element> findElements(Supplier<By> by) {
        return browser.findElements(by);
    }

So if you are definitely expecting an element on the page, please use untilFound method. findElement will not wait. The untilFound method is more suitable in a AJAX environment where timing is very important.

ElementLocator calls untilFound while ElementTryLocator calls findElement,

import com.algocrafts.pages.Element;
import com.algocrafts.pages.Searchable;
import org.openqa.selenium.By;

import java.util.function.Supplier;

public class ElementLocator<Where extends Searchable<Where>> extends AbstractLocator<Where, Element> {

    public ElementLocator(Supplier<By> method) {
        super(method);
    }

    @Override
    public Element find(Where where) {
        return where.untilFound(by);
    }
}


import com.algocrafts.pages.Element;
import com.algocrafts.pages.Searchable;
import org.openqa.selenium.By;

import java.util.function.Supplier;

public class ElementTryLocator<Where extends Searchable<Where>> extends AbstractLocator<Where, Element> {

    public ElementTryLocator(Supplier<By> method) {
        super(method);
    }

    @Override
    public Element find(Where where) {
        return where.findElement(by);
    }
}

The ExplicitWait interface is very useful in providing Explicit wait By wrapping FluentWait, since Java 8, it is legal to provide default method implementation inside interface which made the code even cleaner.

Before Java 8, Google Guava code provided two interfaces, Function and Predicate, to enable functional programming in Java. Selenium FluentWait, uses both Function and Predicate from Guava code.

Should Selenium Capsules sticks to Google Guava, or the Function and Predicate from Java 8? The decision I made is to use Java 8. The reason is simple, Java 8 Collection API has a new addition, Stream, which requires the interface of Java 8 Function and Predicate, so using them is beneficial in utilizing new features from Java 8. Then how to convert the Java 8 into Guava Function and Predicate? Actually that's pretty easy in Java 8, let me show all the possible solutions,

1. Plain Old Anonymous Inner Class

    /**
     * @param duration  timeout duration
     * @param timeUnit  unit
     * @param predicate predicate
     * @throws TimeoutException timeout
     */
    default public void until(int duration, TimeUnit timeUnit, final Predicate<Where> predicate) throws TimeoutException {
        try {
            getFluentWait(duration, timeUnit).until(new com.google.common.base.Predicate<Where>() {
                @Override
                public boolean apply(Where where) {
                    return predicate.test(where);
                }
            });
        } catch (TimeoutException e) {
            save();
            throw e;
        }
    }

2. Lambda Expression

    /**
     * @param duration  timeout duration
     * @param timeUnit  unit
     * @param predicate predicate
     * @throws TimeoutException timeout
     */
    default public void until(int duration, TimeUnit timeUnit, Predicate<Where> predicate) throws TimeoutException {
        try {
            getFluentWait(duration, timeUnit).until((Where where) -> predicate.test(where));
        } catch (TimeoutException e) {
            save();
            throw e;
        }
    }


3. Method Reference

    /**
     * @param duration  timeout duration
     * @param timeUnit  unit
     * @param predicate predicate
     * @throws TimeoutException timeout
     */
    default public void until(int duration, TimeUnit timeUnit, Predicate<Where> predicate) throws TimeoutException {
        try {
            getFluentWait(duration, timeUnit).until(predicate::test);
        } catch (TimeoutException e) {
            save();
            throw e;
        }
    }


You can see, Method Reference is the simplest, Java 8 is a huge step forward.

Monday, October 15, 2012

design of page (500 mcg)

Selenium is used for functional tests of a web application. The purpose of tests are to verify the expected behavior of the pages under certain usage which are human interactions such as typing, clicking, etc.

A page is a collective concepts describe what's displayed on that page,
  • a Button,
  • a list of Buttons,
  • a Header,
  • a list of Links,
  • a list of input,
  • a list of images,etc.
  • Or some actions it can do for you, such as accepting the alert box, move mouse to here and there, find this and find that.

    In the post of "Design of DatePicker", there is some snippet of test code to test the behavior of the jQuery DatePicker on this page, http://jqueryui.com/datepicker/, the complete class is here,

    /**
     *
     * Copyright (c) 2012, Algocrafts, Inc. All rights reserved.
     * Apache License version 2.
     */
    package com.algocrafts.calendar;
    
    import com.algocrafts.calendar.core.DatePicker;
    import com.algocrafts.calendar.jqury.JQueryCalendar;
    import com.google.common.base.Function;
    import org.junit.Before;
    import org.junit.Test;
    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.firefox.FirefoxDriver;
    
    import static com.algocrafts.calendar.month.CapitalizedMonths.September;
    import static org.junit.Assert.assertEquals;
    import static org.openqa.selenium.By.id;
    
    public class JQueryCalendarTest {
    
        WebDriver webDriver;
        DatePicker jQueryCalendarDatePicker;
    
        @Before
        public void setup() {
            webDriver = new FirefoxDriver();
            webDriver.get("http://jqueryui.com/datepicker/");
            jQueryCalendarDatePicker = new DatePicker(
               new JQueryCalendar(webDriver, new JqueryCalendarTriggerFinder())
            );
        }
    
        @Test
        public void testPickADate() {
            jQueryCalendarDatePicker.pick(September, 30, 1999);
            assertEquals("09/30/1999",   webDriver.findElement(id("datepicker")).getAttribute("value"));
        }
    
        private class JqueryCalendarTriggerFinder
          implements Function<WebDriver, WebElement> {
            @Override
            public WebElement apply(WebDriver webDriver) {
                WebElement iframe = webDriver.findElement(By.tagName("iframe"));
                return webDriver.switchTo().frame(iframe).findElement(id("datepicker"));
            }
        }
    } 

    The test is well written and it seems very clean. It follows normal jUnit practice. Most people would like this test. And most tests are written this way.

    There are some inadequateness of the test. What's lacking is a little bit of design. While mixing the test specification with the details of how the testing is conducted. It makes the test more a technical artifact, thus not well suited for Behavior Driven Development.

    There is another way of organizing Selenium tests, by introducing a Page model. The Page model is also an application of Domain Driven Design principle. This approach separates the concern of What to do and How to do it. It makes the tests more domain specific thus more readable.

    Let us have a look of the modified test,

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"/appContext.xml"})
    public class JQueryDatePickerTest extends AbstractPageTest {
    
        @Autowired
        JQueryDatePickerPage jQueryDatePickerPage;
    
        @Before
        public void setup() {
            jQueryDatePickerPage.open();
        }
    
    
        @Test
        public void start() {
            jQueryDatePickerPage.pick(APRIL, 1, 2012);
            assertEquals("04/01/2012",jQueryDatePickerPage.getDate());
        }
    
        @After
        public void close() {
            jQueryDatePickerPage.close();
        }
    }
    
    

    In Java language, one way of measure the complexity of the class is to counting the import statements at the top. The new test only has 4 import statements and 3 lines of real code and there is nothing about Selenium WebDriver,  it is much simpler than the previous version of the test.

    The JQueryCalendarPage class used by the test is listed below, it describes the properties and behavior of the page. Even it is incomplete yet but it illustrates its purpose. By encapsulating page related behavior into a page object, we increased the cohesion of the design.
    To fully understand it, you need to have knowledge of lambda expression in Java 8.
    package com.jquery;
    
    
    import com.algocrafts.calendar.DatePicker;
    import com.algocrafts.calendar.JQueryCalendar;
    import com.algocrafts.clickable.Clickable;
    import com.algocrafts.decorators.AbstractPage;
    import com.algocrafts.decorators.Browser;
    import com.algocrafts.locators.ElementLocator;
    
    import static com.algocrafts.converters.GetText.TEXT;
    import static com.algocrafts.converters.GetText.VALUE;
    import static com.algocrafts.converters.TextEquals.DATEPICKER;
    import static com.algocrafts.searchmethods.ById.CONTENT;
    import static com.algocrafts.searchmethods.ById.DATE_PICKER;
    import static com.algocrafts.searchmethods.ByTagName.H1;
    
    public class JQueryDatePickerPage extends AbstractPage {
    
        public JQueryDatePickerPage(Browser browser, Clickable clickable) {
            super(browser, clickable,
                page -> new ElementLocator<AbstractPage>(CONTENT)
                    .and(new ElementLocator<>(H1))
                    .and(TEXT)
                    .and(DATEPICKER)
                    .apply(page)
            );
        }
    
        private final DatePicker datepicker = new DatePicker(
            new JQueryCalendar(this,
                page -> new ElementLocator<AbstractPage>(DATE_PICKER)
                    .apply(page.frame(0))
            )
        );
    
        public void pick(Enum month, int day, int year) {
            datepicker.pick(month, day, year);
        }
    
        public String getDate() {
            return new ElementLocator<AbstractPage>(DATE_PICKER)
                .and(VALUE)
                .apply(this);
        }
    
    }
    


    One more advantage of this design is the same page object can also used in a jBehave test step,

    /**
     *
     * Copyright (c) 2012, Algocrafts, Inc. All rights reserved.
     * Apache License version 2.
     */
    package com.algocrafts.calendar;
    
    import org.jbehave.core.annotations.Given;
    import org.jbehave.core.annotations.Then;
    import org.jbehave.core.annotations.When;
    import org.junit.Before;
    import org.junit.Test;
    
    import static com.algocrafts.calendar.month.CapitalizedMonths.September;
    import static org.junit.Assert.assertEquals;
    
    public class JQueryCalendarSteps {
    
        JQueryCalendarPage page;
    
        @Given("User is on jQuery Calendar page.")
        public void setup() {
            page = new JQueryDatePickerPage();
        }
    
        @When("user pick $month, $day, $year from calendar")
        public void userPick(String month, int day, int year) {
            page.pick Month.valueOf(month), day, year
        }
    
        @Then("The input displays $date")
        public void verifyDateIs(String date) {
            assertEquals date, page.getDate()
        }
    } 
    

    which can be used to serve the test for a story written in jBehave story format,

     Given user is on jQuery Calendar page.
     When user pick $month, $day, $year from calendar
     |month    |day|year|
     |September|  1|2002|
     |July     | 31|1999|
     |December | 25|2018|
     Then the input displays $date
     |date      |
     |09/01/2002|
     |07/31/1999|
     |12/25/2018|
     
    Not only that, the same page can be used by a Grails Spock test as well,
    /**
     *
     * Copyright (c) 2012, Algocrafts, Inc. All rights reserved.
     * Apache License version 2.
     */
    package com.algocrafts.calendar
    
    import grails.plugin.spock.IntegrationSpec
    
    import static org.junit.Assert.assertEquals
    import static com.algocrafts.calendar.month.CapitalizedMonths.*;
    
    class JQueryCalendarPageSpec extends IntegrationSpec {
    
       JQueryCalendarPage page
    
       def "jQuery Calendar works as expected"(){
    
          given: page = new JQueryCalendarPage()
          when: page.setDate month, day, year
          then: assertEquals date, page.datDate()
    
          where:
    //     |---------|---|----|----------|
           |month    |day|year|date      |
           |September|  1|2002|09/01/2002|
           |July     | 31|1999|07/31/1999|
           |December | 25|2018|12/25/2018|
    //     |---------|---|----|----------|
       }
    }
    

    It is noticeable that the Page object model makes the design more versatile, the same test can be used to serve multiple testing flavors, those tests are more business friendly thus can be used to share the domain knowledge with business users, once business users get familiar with those test syntax, you may be able to convince them to provide pseudo test code. This improves the collaboration between developers and business users and in turn improve the agility of the project.

    Refrences:
    1. PageObject Pattern:  http://code.google.com/p/selenium/wiki/PageObjects
    2. jBehave: http://jbehave.org/
    3. jQuery: http://jquery.com/
    4. Grails Spock: http://code.google.com/p/spock/


    Saturday, October 13, 2012

    design of datepicker (1000 mcg)

    When testing web application, sometime it is necessary to simulate the clicking of a calendar icon and pick a date using JavaScript DatePicker.

    DatePicker is no difference from the other element on web page, usually after clicking the calendar icon, a calendar will be popup and most likely it is a division object on the same page, then user looks at the title of the Calendar and decides whether to click previous year or next year, previous month or next month and then click the day.

    They are many styles of the calendar right now, most popular JavaScript frameworks such as jQuery, dojo and ext-js provide their own implementations. However, even they have different look and feel, they all have these common properties and behaviors,

    1. an icon used as a trigger;
    2. display a calendar when the icon is clicked;
    3. Previous Year button(<<), optional;
    4. Next Year Button(>>), optional;
    5. Previous Month button(<);
    6. Next Month button(>);
    7. day buttons;
    8. Weekday indicators;
    9. Calendar title i.e. "September, 2011" which representing current month and current year.

    Based on these properties and associated behaviors, we can design a generic DatePicker class to be used by all selenium based tests.

    /**
     *
     * Copyright (c) 2012, Algocrafts, Inc. All rights reserved.
     * Apache License version 2.
     */
    package com.algocrafts.calendar;
    
    /**
     * A general purpose DatePicker can be used to pick a given date from
     * the calendar flyout provided by JavaScript framework.
     *
     * @author Yujun Liang
     * @since 0.1
     */
    public class DatePicker {
    
        private final Calendar calendar;
    
        /**
         * Constuctor of the DatePicker which taking a Calendar interface.
         *
         * @param calendar
         */
        public DatePicker(Calendar calendar) {
            this.calendar = calendar;
        }
    
        /**
         * Pick a date by the given parameter.
         * for example,
         * datePicker.pick(AMonthEnum.July, 31, 1999) or for another style,
         * datePicker.pick(AnotherMonthEnum.JULY, 31, 1999), or
         * datePicker.pick(AbbreviatedMonthEnum.Sep, 31, 1999)
         * datePicker.pick(AnotherAbbreviatedMonthEnum.SEPT, 31, 1999)
         * the month just need to be same as the text on the calendar, if it looks like
         * Sep. on the calendar, you can either strip out the dot before the enum valueOf
         * call, or define an enum like this Sep("Sep.") and then provide a lookup map to
         * resolve the enum Sep from "Sep.".
         *
         * @param month, it need to be defined as an enum to make the code cleaner.
         * @param day,   an integer representing the day appearing on the calendar
         * @param year,  an ineger representing the year appearing on the calendar
         */
        public void pick(Enum month, int day, int year) {
            calendar.show();
            calendar.enterYear(year);
            calendar.enterMonth(month);
            calendar.pickDay(day);
        }
    
    
    The calendar is an interface, it can be implemented based on the DatePicker used by each project.

    /**
     *
     * Copyright (c) 2012, Algocrafts, Inc. All rights reserved.
     * Apache License version 2.
     */
    package com.algocrafts.calendar;
    
    /**
     * This is the interface describing the behaviour of a calendar flyout
     * which can be operated by a DatePicker.
     *
     * @author Yujun Liang
     * @since 0.1
     */
    public interface Calendar {
    
        /**
         * The action making the calendar visible.
         */
        void show();
    
        /**
         * 
         * @return current year displayed on Calendar.
         */
        int currentYear();
    
        /**
         * @return the ordinal of the enum instance for current month on Calendar.
         */
        int currentMonth();
    
        /**
         * Clicking next year button once, or clicking the next month button
         * 12 times if the next year button is not present on the calendar.
         */
        void nextYear();
    
        /**
         * Clicking next month button once.
         */
        void nextMonth();
    
        /**
         * Clicking previous month button once.
         */
        void previousMonth();
    
        /**
         * Clicking previous year button once, or clicking the previous month
         * button 12 times if the next year button is not present on the calendar.
         */
        void previousYear();
    
        /**
         * Some calendar allows user to select a year from a dropdown(select) or
         * enter a value from an input field. This method is to cater that function.
         * @param year
         */
        void enterYear(int year);
    
        /**
         * Some calendar allows user to select a month from a dropdown(select) or
         * enter a value from an input field. This method is to cater that function.
         * @param month
         */
        void enterMonth(Enum month);
    
        /**
         * After flipping the calendar to the month user is picking, clicking
         * the day button.
         * @param day
         */
        void pickDay(int day);
    }
    

    Some calendar does provide direct method to enter year and month, so we need a general purpose Flipping Method to click the buttons to select year and month.
    /**
     *
     * Copyright (c) 2012, Algocrafts, Inc. All rights reserved.
     * Apache License version 2.
     */
    package com.algocrafts.calendar;
    
    /**
     * For some calender, if no direct handle allow you to change year and month,
     * user need to click the year and month button to flip the calendar to the
     * desired year and month. This class provide such functionality.
     */
    public class FlippingMethod {
    
        private final Calendar calendar;
    
        /**
         * Associate this with the calendar.
         *
         * @param calendar
         */
        public FlippingMethod(Calendar calendar) {
            this.calendar = calendar;
        }
    
        /**
         * flip to the year like counting the click by heart with eye closed to save the time
         * of reading the calender again since reading the calendar is more expensive then
         * maitaining a counter.
         *
         * @param year
         */
        public void flipToYear(int year) {
            int yearDiffrence = calendar.currentYear() - year;
            if (yearDiffrence < 0) {
                for (int i = yearDiffrence; i < 0; i++) {
                    calendar.nextYear();
                }
            } else if (yearDiffrence > 0) {
                for (int i = 0; i < yearDiffrence; i++) {
                    calendar.previousYear();
                }
            }
        }
    
        /**
         * flip to the month like counting the click by heart with eye closed to save the time
         * of reading the calender again since reading the calendar is more expensive then
         * maitaining a counter.
         *
         * @param month
         */
        public void flipToMonth(Enum month) {
            int monthDifference = calendar.currentMonth() - month.ordinal();
            if (monthDifference < 0) {
                for (int i = monthDifference; i < 0; i++) {
                    calendar.nextMonth();
                }
            } else if (monthDifference > 0) {
                for (int i = 0; i < monthDifference; i++) {
                    calendar.previousMonth();
                }
            }
        }
    }
    
    
    There is no Selenium used in DatePicker, Calendar and FlippingMethod at all. That's right, this is called Dependency Injection. You may have already heard of this pattern if you have worked on any project using Spring framework or read the blog from Martim Fowler. The following implementation can be injected into DatePicker during runtime and you can inject different Calendar depending on what JavaScript library you are using. You don't even need to use WebDriver, you can still implement your calendar using Selenium-RC which has been deprecated in favor of new WebDriver API in Selenium 2, which is another example of the same principle.

    Here is the actual implementation using Selenium WebDriver api against this Datepicker on jQuery's website,  http://jqueryui.com/datepicker/

    /**
     *
     * Copyright (c) 2012, Algocrafts, Inc. All rights reserved.
     * Apache License version 2.
     */
    package com.algocrafts.calendar;
    
    import com.google.common.base.Function;
    import com.google.common.base.Predicate;
    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.ui.WebDriverWait;
    
    import java.util.List;
    
    import static com.algocrafts.calendar.JQueryCalendar.Months.valueOf;
    import static com.google.common.base.Predicates.not;
    import static java.lang.Integer.parseInt;
    import static org.openqa.selenium.By.className;
    import static org.openqa.selenium.By.tagName;
    
    /**
     * This is the reference implmetnation of the Calendar interface which can be
     * operated by a DatePicker.
     * The location of the date picker is here,
     * http://jqueryui.com/datepicker/
     *
     * @author Yujun Liang
     * @since 0.1
     */
    public class JQueryCalendar implements Calendar {
    
        /**
         * An enum of all months used by that calendar
         */
        public enum Months {
            January,
            February,
            March,
            April,
            May,
            June,
            July,
            August,
            September,
            October,
            November,
            December;
        }
    
        /**
         * Constructor of the JQueryCalendar, an active WebDriver and a search
         * criteria of the trigger element.
         *
         * @param webDriver
         * @param trigger
         */
        public JQueryCalendar(WebDriver webDriver, Function<WebDriver, WebElement> trigger) {
            this.webDriver = webDriver;
            this.trigger = trigger;
        }
    
        private final WebDriver webDriver;
        private final Function<WebDriver, WebElement> trigger;
    
            @Override
        public void show() {
            trigger.apply(webDriver).click();
            new WebDriverWait(webDriver, 60).until(new CalendarIsDisplayed());
        }
    
        @Override
        public int currentYear() {
            return parseInt(calendar().findElement(className("ui-datepicker-year")).getText());
        }
    
        @Override
        public int currentMonth() {
            return valueOf(
                       calendar().findElement(className("ui-datepicker-month")).getText()
                   ).ordinal();
        }
    
        @Override
        public void previousYear() {
            for (int i = 0; i < 12; i++) {
                previousMonth();
            }
        }
    
        @Override
        public void previousMonth() {
            calendar().findElement(className("ui-datepicker-prev")).click();
        }
    
        @Override
        public void nextYear() {
            for (int i = 0; i < 12; i++) {
                nextMonth();
            }
        }
    
        @Override
        public void nextMonth() {
            calendar().findElement(className("ui-datepicker-next")).click();
        }
    
        @Override
        public void enterYear(int year) {
            flippingMethod.flipToYear(year);
        }
    
        @Override
        public void enterMonth(Enum month) {
            flippingMethod.flipToMonth(month);
        }
    
        @Override
        public void pickDay(int day) {
            List<WebElement> dayButtons =
                    calendar().findElement(
                            className("ui-datepicker-calendar")
                    ).findElements(tagName("td"));
            for (WebElement dayButton : dayButtons) {
                if (dayButton.getText().equals(String.valueOf(day))) {
                    dayButton.click();
                    new WebDriverWait(webDriver, 60).until(not(new CalendarIsDisplayed()));
                }
            }
        }
    
        private WebElement calendar() {
            return webDriver.findElement(By.id("ui-datepicker-div"));
        }
    
        /**
         * Predicate needed by WebDriverWait for Calendar to become visible
         * it can be used for Calendar to become invisible as well, simply
         *   Predicates.not(new CalendarIsDisplayed()) as used in method 
         *   pickDay.
         */
        private class CalendarIsDisplayed implements Predicate<WebDriver> {
            @Override
            public boolean apply(WebDriver webDriver) {
                return calendar() != null && calendar().isDisplayed();
            }
        }    
    }
    
    In your test, where you have access to Selenium WebDriver webDriver, you can simply instantiate the DatePicker,
    jQueryCalendarDatePicker = new DatePicker(
                                   new JQueryCalendar(
                                       webDriver, 
                                       new JQueryCalendarTriggerFinder()
                                   )
                               );
        
    jQueryDatePicker.pick(July, 23, 1999);
    jQueryDatePicker.pick(September, 2, 2018);
    
    These classes have this relationship as illustrated by the following class diagram,
    The test ran and passed, it took a while to go back to 1999 since there is no previous year button available so the behavior of the previous year is actually implemented by clicking previous month 12 times.
    If you noticed, the parameter for a date is in 3 parts, an enum of the month, an integer day and integer year, the reason behind it is to make the code more readable, it is the application of Domain Driven Design principle, Ubiquitous Language. I prefer it over a java.util.Date as the parameter. Also, if I had chosen to use java.util.Date as the parameter, the implementation of the DatePicker would have been more complex. As an exercise, you can try that at home.

    If you use another JavaScript framework, the calendar may have different elements, you can implement another Calendar and use the same DatePicker to pick any Date in the range provided by the calendar.

    Dojo,
    DatePicker dojoDatePicker = 
       new DatePicker(new DojoCalendar(webDriver, new DojoCalendarTriggerFinder()));
    dojoDatePicker.pick(July, 23, 1999);
    dojoDatePicker.pick(September, 2, 2018);
    
    public class DojoCalendar implements Calendar {
       ... //Jonathan, sorry for using ...   
    }
    
    YUI,
    DatePicker yuiDatePicker = 
       new DatePicker(new YuiCalendar(webDriver,new YuiCalendarTriggerFinder()));
    
    yuiDatePicker.pick(July, 23, 1999);
    yuiDatePicker.pick(September, 2, 2018);
    
    public class YuiCalendar implements Calendar {
       ... //sorry again.   
    }
    
    Here is another style of the calendar,
    since it provides the direct method to pick year and month, the implementation is slightly different from JQueryCalendar, it uses a Select to change the year. So in this class, it doesn't need to instantiate an instance of FlippingMethod.
        @Override
        public void enterYear(int year) {
            yearSelect().selectByVisibleText(String.valueOf(year));
        }
    
        /**
         * //TODO: use firebug to find out THE NAME OF THE SELECT OF YEAR  
         * @return
         */
        private Select yearSelect() {
            return new Select(webDriver.findElement(By.name("THE NAME OF THE SELECT OF YEAR")));
        }
    
        @Override
        public void enterMonth(Enum month) {
            monthSelect().selectByVisibleText(month.name());
        }
    
        /**
         * //TODO: use firebug to find out THE NAME OF THE SELECT OF MONTH  
         * @return
         */
        private Select monthSelect() {
            return new Select(webDriver.findElement(By.name("THE NAME OF THE SELECT OF MONTH")));
        }
    
    Why bother to design this DatePicker class with an interface, not just use inheritance with an abstract PickPicker for shared logic such as flipping calendar to desired year and month?

    References:
    1. jQuery : http://jquery.com/
    2. Selenium : http://seleniumhq.org/
    3. Martin Fowler : http://martinfowler.com/
    4. Domain Driven Design : http://www.domaindrivendesign.org/