Saturday, November 30, 2013

locator pattern (100 mcg)

Selenium By API provides a good way to search for element on web page. However, it doesn't support complex search unless you use By.ByXPath. I don't like to use ByXPath, I think it cluttered the code with too many string literals. I found Locator pattern quite useful in improving the readability of the code so I designed a Locator interface in Selenium Capsules.
public interface Locator<Where, What> extends Function<Where, What> {

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Locator<Where, V> and(Locator<? super What, ? extends V> after) {
        Objects.requireNonNull(after);
        return (Where t) -> after.apply(apply(t));
    }
}

The locator is an extension of function object that became available in Java 8. An example of the Selector and a Locator Factory to return a locator of your choice.
public enum ClassName implements Supplier<By> {

    SF_JS_ENABLED("sf-js-enabled"),

    UI_DATEPICKER_CALENDAR("ui-datepicker-calendar"),
    UI_DATEPICKER_NEXT("ui-datepicker-next"),
    UI_DATEPICKER_PREV("ui-datepicker-prev"),
    UI_DATEPICKER_MONTH("ui-datepicker-month"),
    UI_DATEPICKER_YEAR("ui-datepicker-year"),
    UI_DATEPICKER_HEADER("ui-datepicker-header"),

    PAGE_TITLE("page-title"),
    UI_DATEPICKER_CLOSE("ui-datepicker-close");

    private final By by;

    private ByClassName(String id) {
        this.by = className(id);
    }

    @Override
    public By get() {
        return by;
    }

    @Override
    public String toString() {
        return by.toString();
    }
}

public class Locators<Where extends Searchable<Where>, What>
        implements Locator<Where, What> {

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

    public static <Where extends Searchable<Where>> Locators<Where, Stream<Element>> elements(Supplier<By> selector) {
        return new ElementsLocator<>(selector);
    }

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

    public static SelectLocator select(Supplier<By> selector) {
        return new SelectLocator(selector);
    }

    private final Locator<Where, What> locator;

    public Locators(Locator<Where, What> locator) {
        this.locator = locator;
    }

    @Override
    public What locate(Where where) {
        return locator.locate(where);
    }
}

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

    public ElementLocator(Supplier<By> selector) {
        super((Where where) -> where.untilFound(selector));
    }
}
With this Locator pattern, we can have an Input class for the <input/> tag,
package com.algocrafts.formelements;


import com.algocrafts.algorithm.Retry;
import com.algocrafts.capsules.Element;
import com.algocrafts.capsules.Searchable;
import com.algocrafts.converters.GetText;
import com.algocrafts.locators.Locator;
import org.apache.log4j.Logger;

import java.util.function.Predicate;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.apache.log4j.LogManager.getLogger;

public class Input<T extends Searchable<T>> {

    private static final Logger log = getLogger(Input.class);

    private final T form;

    public Input(T form) {
        this.form = form;
    }

    public void put(final Locator<T, Element> locator, final Object value) {
        String string = value.toString();
        log.info("setting input[" + locator + "]=[" + string + "]");
        final Retry retry = new Retry(5, 1, SECONDS);
        try {
            retry.attempt(() -> {
                locator.apply(form).clear();
                locator.apply(form).sendKeys(string);
                if (locator.and(GetText.VALUE).apply(form).equals(string)) {
                    retry.off();
                }
                return null;

            });
        } catch (Exception e) {
            log.info(String.format("Failed to set text %s to %s", string, locator), e);
        }
    }

}
In test class, for example, http://seleniumcapsules.blogspot.com/2013/01/refactor-selenium-example-using-page.html, if there are elements with the same name on a form, it is not possible to use findElementByName method, we need to use alternative API to locate the element, instead of provide other method such as typeByName, typeById and typeByXpath, we can use this type(Locator locator, Object value) to simplify the API, and the test became something like this, using an overloaded type method,
public class LoginPage extends AbstractPage{
 
    public HomePage loginAs(String username, String password) {
        put(USER_NAME, username);
        put(PASSWORD, password);
        button("login").submit();
        return new HomePage();
    }
}
Where USER_NAME and PASSWORD are enum instances in the above IdLocator

public class AbstractPage  {

    ...

    public void put(Locator<AbstractPage, Element> locator, Object value) {
        new Input<AbstractPage>(this).put(locator, value);
    }

    ...

}

public class AbstractForm  {

    ...

    public void put(Locator<AbstractForm, Element> locator, Object value) {
        new Input<AbstractForm>(this).put(locator,value);
    }

    ...

}
The developers of the Selenium may also realize that the By class is not enough to conduct a search on a page, if more complex scenario is required so they came up with another search method, ByChained, however, this class still doesn't address the issue, if we want to find an element and find all elements on it which meeting certain criteria, Locator solved this problem gracefully.

Locator is a generic interface which is a function of convert A to B, so we can define it operates on any A which can be WebDriver, WebElement, etc and define B as WebElement, List<WebElement> or Stream<WebElement>, where to search from and what to search for are entirely up to you, so the parameter type for the interface is <Where, What> rather than the standard < as appeared in the Function interface.

Here are some examples of the locators, these two enum instance of CURRENT_YEAR and CURRENT_MONTH return the year and month on the calendar,



public enum CalendarIntegerLocator implements Locator<AbstractPage, Integer> {

    /**
     * Locate the integer value representing current year on a calendar
     */
    CURRENT_YEAR(
            Locators.<AbstractPage>element(UI_DATEPICKER_DIV)
                    .and(element(UI_DATEPICKER_HEADER))
                    .and(element(UI_DATEPICKER_YEAR))
                    .and(TEXT)
                    .and(PARSE_INT)
    ),


    /**
     * Locate the integer value representing current month on a calendar
     */
    CURRENT_MONTH(
            Locators.<AbstractPage>element(UI_DATEPICKER_DIV)
                    .and(element(UI_DATEPICKER_MONTH))
                    .and(TEXT)
                    .and(TO_MONTH)
                    .and(ORDINAL)
    );

    private Locator<AbstractPage, Integer> locator;

    private CalendarIntegerLocator(Locator<AbstractPage, Integer> locator) {
        this.locator = locator;
    }

    @Override
    public Integer locate(AbstractPage page) {
        return locator.locate(page);
    }
}


Here is a more complex locator which combines mouseover event to locate hidden menu which only displays when you mouse is over its group header,

public class MouseOverLocator implements Locator<AbstractPage, Element> {

    private final String menuGroup;
    private final String menuItem;

    public MouseOverLocator(String menuGroup, String menuItem) {
        this.menuGroup = menuGroup;
        this.menuItem = menuItem;
    }

    public Element locate(AbstractPage page) {
        return Locators.<AbstractPage>element(MAIN_NAV)
                .and(element(SF_JS_ENABLED))
                .and(elements(LI))
                .and(new FirstMatch<>(DISPLAYED.and(TEXT.and(new IsStringEqual(menuGroup)))))
                .and(page.mouseOver())
                .and(element(UL))
                .and(element(() -> linkText(menuItem)))
                .locate(page);
    }

    @Override
    public String toString() {
        return "[" + menuGroup + "->" + menuItem + "]";
    }
}



First, it locates an element E_A using MAIN_NAV, then locates another element on element E_A using SF_JS_ENABLED, then locates all element iwht <li> tag and find the first element whose text is same as menuGroup, then overOver it and find another element E_B whose tag is <ul> and on element E_B, find the element whose linkText is menuItem. That's how you can find the it, it is quite complex but it is not impossible. This technical is called function composition, it compose a series of simple functions into a complex function and operates the final function on the given subject and returns the final result of the function computation.





        Clickable menu = new Menu(homePage, new MouseOverLocator("Features", "Events Management"));
        menu.click();


The code above will find the "Events Management" menu and click it. Also, you can have a page return all the menu items and click them one by one,

Sunday, March 31, 2013

test report generation (50 mcg)

Report generation is a common function of the applications especially financial applications. For example, all the banks provide statement download function through their websites. I am not sure how they test that functionality, this is what I did for one of my client.

The report downloading process contains following steps, thus a template method or composite approach will be suitable to design a generic ReportDownload, let me design an interface first,

public interface Report{
}

public interface ReportDownload {
   void navigate();
   void enterCriteria(ReportCriteria criteria);
   void triggering();
   Clickable trigger();
   Report findReport();
   Report resolveExpection(Report actual)
}

Report is a type, based on its file type, it can be a CSV, Excel or PDF document.

This is an article about how to save the downloaded file a certain folder, selenium forefox profile, with design, we can add a variable to hold the directory of the download files as per test base, so the findReport() method can look into that directory to find newly arrived file, in that way, you don't need to specify the download file name in the test. To test the result, a simple way is to use pre generation file as an expectation to compare the downloaded report. The expectation can be stored in a classpath directory which can be resolved by the file name of the actual report.

Once expectation and actual report are founded, we can compare two files to find out whether they are same, we can override the equals method to compare the content of the two files.


Report actual = page.findReport();
Report expected = page.resolveExpectation();
assertEquals(expected, actual)

Saturday, March 23, 2013

tackle the incompatibility of selenium jar file with firefox binary version (120 mcg)

If we don't specify the firefox binary location, Selenium web driver will use the default firefox binary location, on Windows, it is C:\Program Files\Mozilla\Firefox\. When firefox upgrade to a newer version, the web driver may fail to trigger the firefox since some API may be changed. Selenium developers will release a newer version of selenium jar file for you to upgrade, but unless you upgrade you dependency file, the test will not work and it can cause frustration.

There is a way to overcome this, to include firefox binary in a jar file and add a maven step to explode it to file system before starting the tests, and you can also include a customized profile in the jar file so it can support file download of known file formats, for example, CSV, PDF and Excel formats. It also saves each individual developer's time in manipulating profile which is also error prone. You can include firebug plugin with the binary so you can use it to examine web elements when running the test on a debug mode.

if the above step is too difficult to achieve, there is another simpler approach, just copy target firefox binary and customized profiles to a location and pass the location as a JVM option variable to the test, the test class then load the firefox binary from the location,


   public static WebDriver getWebDriver() {
      FirefoxBinary firefox = new FirefoxBinary(new File(System.getProperty("firefox.binary"));
      FirefoxProfile profile = new FirefoxProfile(new File(System.getProperty("firefox.profile")
      return new FirefoxDriver(firefox, profile);
   }
This is the constructor used by the method.

and set the following JVM options variables,

   -Dfirefox.binary=/projects/test/firefox
   -Dfirefox.profile=/projects/test/profile



/projects/test/firefox is the unix style for file system folder, it works for Windows as well, it points to C:\projects\test\firefox, where firefox.exe is located for the tests.

This solved the problem caused by the incompatibility of web driver and firefox browser version and make the test more stable.

This solution has evolved into a more advance form, a browser factory using enum

use enum to increase the readability of the test (50 mcg)

Using enum can greatly increase the readability of the code and it may significantly reduce the complexity as well. Let us start with some code not using enum,
    DateUtils.getDate(2012, 5, 6)
It is not clear which month it is. Does it mean May or June? Worse thing is in one library it may mean May and in another library it means June. I added another method to make the following call possible, with static import in Java, it look like this,
    date(June, 6, 2012)
I believe even non technical people can read it and understand now, it make the code much easier to understand.
   public enum Months {
       January,
       February,
       March,
       April,
       May,
       June,
       July,
       August,
       September,
       October,
       November,
       December;
   }
I don't need to rewrite new method, I just need to call the old method in the new method,
    static Date date(Enum month, int day, int year) {
       DateUtils.getDate(year, month.ordinal(), day)
    }
Sometimes, on the web layer, the month may be spelled as a short form, then we can introduce another enum,
   public enum ShortMonths {
       Jan,
       Feb,
       Mar,
       Apr,
       May,
       Jun,
       Jul,
       Aug,
       Sep,
       Oct,
       Nov,
       Dec;
   }
We can pass this ShortMonths and make the call like this,
    date(Jun, 6, 2012)
DatePicker uses this pattern.

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,