The abstract object based Selenium Test framework (AOST)

By johnjianfang

The abstract object based Selenium Test framework (AOST)  is designed not only for user acceptance test (UAT) but also for more general UI tests for developers since nowadays more and more people work in an agile environment. The requirement keeps changing and people keep refactoring and testing the UI. Selenium tests are usually created by using Selenium IDE to record Web UI Events and then replay them. The disadvantages include Fragile, not really work in a dynamic environment. It is not module based and hence difficult to refactor and maintain.

The AOST test framework does not depend on Selenium IDE at all. It uses UI components to modularize and to abstract the UIs.  Furthermore, you can start to write UI tests while you are doing code development. Other advantages include reusable, expressive, easier to refactor and maintain.

The AOST framework introduces a new Domain Specific Language (DSL) and an object to locator mapping (OLM) framework to make it much easier for users to write UI tests. The framework has the following features:

The project is located at

http://code.google.com/p/aost

For detailed introduction, please see

http://code.google.com/p/aost/wiki/Introduction

Tags: , , , , ,

2 Responses to “The abstract object based Selenium Test framework (AOST)”

  1. johnjianfang Says:

    The 0.3.0 verion is just out, which includes an Object to Locator Mapping framework (OLM),. With that, AOST can automatically generate the UI object locator for you. AOST also implements the group locating concept to utilize a group of UI objects to help locating their locators. The new version also add the composite locator so that user specifies a set of parameters for the object and the actual locator will be derived automatically by AOST. The new version also supports multiple UI modules in a single DslContext.

    The Group Locating Concept (GLC) concept comes from the simple observation, i.e., it is much easier to locate a collection of UI objects than a single UI object. The reason is multiple UI objects provide more search criteria to help you to locate the UIs. This concept is implemented in AOST. All collection type object, i.e., Container and its extended classes have an option to use the group locating concept.

    Let’s see the differences between regular locating and group locating. Still take the google start page as an example:

    ui.Container(uid: “google_start_page”, clocator: [tag: "td"], group: “true”){
    InputBox(uid: “searchbox”, clocator: [title: "Google Search"])
    SubmitButton(uid: “googlesearch”, clocator: [name: "btnG", value: "Google Search"])
    SubmitButton(uid: “Imfeelinglucky”, clocator: [value: "I'm Feeling Lucky"])
    }

    Here, clocator stands for composite locator. Without group locating, I can only use the given criteria

    tag = “td”

    to find the container locator, i.e.,

    I am looking for a td html tag in the DOM

    which is quite difficult. If we use the group locating, the Container can use the group information provided by its children. Now, the problem becomes:

    I am looking for a td html tag in the DOM and its children including an input box with
    title “Google Search”, a submit button with name “btnG” and value “Google Search”, and
    another submit button with value “I’m Feeling Lucky”

    Woo! I have a lot of information to help me to locate the container. Once the container is found, all its children can be found very easily.

    The direct result of group locating is that for most case, the information defined in the UI module itself is enough for you to locate all UI objects in that UI module. That is to say, your test code only depends on your own UI module, not its location and its outside. Even we change the UI, the test may still work.

    Another advantage is that you may be able to map your JSP, PHP, ASP,…, file directly to the UI module in AOST. This could help developers a lot for writing Selenium tests because they do not need to manually find the locator for an UI object.

    I added FAQ at:

    http://code.google.com/p/aost/wiki/FAQ

    Introduction at

    http://code.google.com/p/aost/wiki/Introduction

    and Tutorial at:

    http://code.google.com/p/aost/wiki/Tutorial

  2. johnjianfang Says:

    Groovy Patterns Used in AOST

    In AOST(http://code.google.com/p/aost), we used many Groovy patterns. I like to list some of them as follows,

    1) BuildSupport

    BuildSupport is very powerful for parsing nested definitions, such as XML markup. In AOST, the buildsupport is used as UI object definition parser and it is one of the bases for our internel DSL.

    UiDslParser extends BuilderSupport{

    protected void setParent(Object parent, Object child) {
    if(parent instanceof Container){
    parent.add(child)
    child.parent = parent
    }
    }

    protected Object createNode(Object name) {
    def builder = builderRegistry.getBuilder(name)

    if(builder != null){
    def obj = builder.build(null, null)

    return obj
    }

    return null
    }

    //should not come here for Our DSL
    protected Object createNode(Object name, Object value) {

    return null
    }

    protected Object createNode(Object name, Map map) {
    def builder = builderRegistry.getBuilder(name)

    if(builder != null){
    def obj = builder.build(map, null) return obj
    }

    return null
    }

    protected Object createNode(Object name, Map map, Object value) {
    def builder = builderRegistry.getBuilder(name)

    if(builder != null){
    def obj = builder.build(map, (Closure)value)

    return obj
    }

    return null
    }

    protected void nodeCompleted(Object parent, Object node) {
    //when the node is completed and its parent is null, it means this node is at the top level
    if(parent == null){
    UiObject uo = (UiObject)node
    //only put the top level nodes into the registry
    registry.put(uo.uid, node)
    }

    }

    }

    2) Dynamic Scripting

    In Groovy, you can define Groovy code as a String and then run them at run time, i.e, code generates code, which makes pure DSL tests possible. Our DslScriptExecutor? is a good example and can demonstrate the power of the dynamic scripting.

    class DslScriptExecutor {

    static void main(String[] args){
    if(args != null && args.length == 1){
    def dsl = new File(args[0]).text
    def script = “”"
    import aost.dsl.DslScriptEngine

    class DslTest extends DslScriptEngine{
    def test(){
    init()
    ${dsl}
    shutDown()
    }
    }

    DslTest instance = new DslTest()
    instance.test()
    “”"

    new GroovyShell().evaluate(script)

    }else{
    println(“Usage: DslScriptExecutor dsl_file”)
    }

    }
    }

    3) GroovyInterceptable

    GroovyInterceptable can intercept all method calls so that you can do some processing before or after the invocation. Or you can delegate the method calls to another class.

    For example, The AOST dispatcher will delegate all method calls it received to Selenium Client as follows,

    class Dispatcher implements GroovyInterceptable{

    private SeleniumClient sc = new SeleniumClient()

    def invokeMethod(String name, args)
    {
    return sc.client.metaClass.invokeMethod(sc.client, name, args)
    }
    }

    4) methodMissing

    In Groovy, you can use “methodMissing” to intercept and delegate undefined method calls. For example, in DslScriptEngine?, the “methodMissing” method will catch and delegate all methods to DslAostSeleneseTestCase? except the “init”, “openUrl”, and “shutDown” methods:

    class DslScriptEngine extends DslContext{

    protected def methodMissing(String name, args) {
    if(name == “init”)
    return init()
    if(name == “openUrl”)
    return openUrl(args)
    if(name == “shutDown”)
    return shutDown()

    if(DslAostSeleneseTestCase.metaClass.respondsTo(aost, name, args)){
    return aost.invokeMethod(name, args)
    }

    throw new MissingMethodException(name, DslScriptEngine.class, args)
    }
    }

    5) Singleton

    You can Use Groovy MetaClass to define a singleton, which is the right Groovy way to define a singleton. For example, we have the EventHandler class

    class EventHandler{
    }

    Then, we can define its MetaClass as follows,

    class EventHandlerMetaClass extends MetaClassImpl{

    private final static INSTANCE = new EventHandler()

    EventHandlerMetaClass() { super(EventHandler) }

    def invokeConstructor(Object[] arguments) { return INSTANCE }
    }

    After that, we register the metaClass:

    def registry = GroovySystem.metaClassRegistry
    registry.setMetaClass(EventHandler, new EventHandlerMetaClass())

    In this way, all

    Eventhandler handler = new EventHandler()

    will return the same Instance, i.e., it is a singleton. Our AOST framework heavily depends on this pattern so that we do not need to use another framework to wiring different object together.

    5) GString

    GString can be used to embed variable in a String and the value will be resolved at run time. This makes it very convenient to write XPath template. For example, In the Selector object, we have

    class Selector extends UiObject {

    def selectByLabel(String target, Closure c){
    c(locator, “label=${target}”)
    }

    def selectByValue(String target, Closure c){

    c(locator, “value=${target}”)()
    }
    }

    6) Optional Type

    In Groovy you can specify a variable type if you know the type and you want to do Type check at compile time. If you do not care about the type or want the method to be more flexible, you do not need to define the type and just use “def” to define a variable without specifying its type.

    For example, in the composite locator class we specified the Type of all variables except the position. Because type of UI component position might be a single type.

    class CompositeLocator {
    String header
    String tag
    String text
    String trailer
    def position
    Map attributes = [:]
    }

    7) Groovy Syntax

    In Groovy, the syntax is very expressive, for example, the DslContext? class defines a lot of methods such as:

    def doubleClick(String uid){}

    and it can be written as

    doubleClick uid

    and the above is one of the basic DSLs for AOST.

    The Map definition is also very expressive and useful. AOST locators use map to define xpath or UI component attributes. For example:

    ui.Container(uid: “google_start_page”, clocator: [tag: "td"], group: “true”){
    InputBox(uid: “searchbox”, clocator: [title: "Google Search"])
    SubmitButton(uid: “googlesearch”, clocator: [name: "btnG", value: "Google Search"])
    SubmitButton(uid: “Imfeelinglucky”, clocator: [value: "I'm Feeling Lucky"])
    }

Leave a Reply