Skip to content

Selenide Comparison

Stefan Ludwig edited this page May 9, 2016 · 2 revisions

On this page we will compare WebTester's approach to page objects with Selenide's. More information about Selenide can be found on the official website.

Page Object Code

Lets take a look at how page objects are defined:

Selenide

public class LoginPage {

    // NOTE: you could static import all Selenide.XXX static methods

    /* workflows */

    public WelcomePage login (String username, String password) {
        return setUsername(username).setPassword(password).clickLogin();
    }

    /* actions */

    public LoginPage setUsername (String username) {
        Selenide.$("#input_un").setValue(username);
        return this;
    }

    public LoginPage setPassword (String password) {
        Selenide.$("#input_p").setValue(password);
        return this;
    }

    public WelcomePage clickLogin () {
        Selenide.$("#submit").click();
        return Selenide.page(WelcomePage.class);
    }

}

WebTester

// possibility #1 - with @IdentifyUsing
public interface LoginPage extends Page {

    @IdentifyUsing("#input_un")
    TextField username();
    @IdentifyUsing("#input_p")
    PasswordField password();
    @IdentifyUsing (value="submit", how = Id.class)
    Button submit();

    /* workflows */

    default WelcomePage login (String username, String password) {
        return setUsername(username).setPassword(password).clickLogin();
    }

    /* actions */

    default LoginPage setUsername (String username) {
        username().setText(username);
        return this;
    }

    default LoginPage setPassword (String password) {
        password().setText(password);
        return this;
    }

    default WelcomePage clickLogin () {
        submit().click();
        return create(WelcomePage.class);
    }

}

// possibility #2 - minimal with Ad-Hoc finding
public interface LoginPage extends Page {

    /* workflows */

    default WelcomePage login (String username, String password) {
        return setUsername(username).setPassword(password).clickLogin();
    }

    /* actions */

    default LoginPage setUsername (String username) {
        find("#input_un").clear().sendKeys(username);
        return this;
    }

    default LoginPage setPassword (String password) {
        find("#input_p").clear().sendKeys(password);
        return this;
    }

    default WelcomePage clickLogin () {
        find("#submit").click();
        return create(WelcomePage.class);
    }

}

The main differences between WebTester and Selenide are:

  • Declarative approach. Define pages and page fragments as interfaces and describe what they should do - we do the rest.
  • Composition over Inheritance. Compose your pages and fragments by extending other interfaces.
  • WebTester allows for the nesting of page fragments within another.
  • WebTester's out of the box elements only offer methods which are appropriate for the given elements type (cant set the value of a Button).
  • Multi-Threading behavior is not hidden behind static classes, instead we make the Browser instances the centers of control over page creation and configuration management. (at the cost of leaving thread management to the developer / tester)
  • Our EventSystem for firing and reacting to events within the framework and your extensions.
  • Our debug markings for highlighting every object the framework has interacted with.
  • Our very conservative use of static classes / methods in order to be as customizable and integrateable as possible.

Test Code

Now lets take a look at how this impacts your test code:

Selenide

public class LoginTest  {

    // NOTE: you could static import all Selenide.XXX static methods

    LoginPage startPage;

    /* life cycle*/

    @Before
    public void initStartPage () {
        Selenide.open("http://localhost:8080/login");
        startPage = Selenide.page(LoginPage.class);
    }

    /* tests */

    @Test
    public void testValidLogin () {
        WelcomePage page = startPage.login("username", "123456");
        assertThat(page.getWelcomeMessage(), is("Hello World!"));
    }

    @Test
    public void testInvalidLogin_Password () {
        LoginPage page = startPage.loginExpectingError("username", "bar");
        assertThat(page.getErrorMessage(), is("Wrong Credentials!"));
    }

}

WebTester

@RunWith(WebTesterJUnitRunner.class) 
public class LoginTest  {

    // This example uses the webtester-support-junit module
    // in order to get rid of boilerplate life cycle management code.

    @Resource
    @CreateUsing(ProjectBrowserFactory.class)
    @EntryPoint("http://localhost:8080/login")
    static Browser browser;

    LoginPage startPage;

    /* life cycle*/

    @Before
    public void initStartPage () {
        startPage = browser.create(LoginPage.class);
    }

    /* tests */

    @Test
    public void testValidLogin () {
        WelcomePage page = startPage.login("username", "123456");
        assertThat(page.getWelcomeMessage(), is("Hello World!"));
    }

    @Test
    public void testInvalidLogin_Password () {
        LoginPage page = startPage.loginExpectingError("username", "bar");
        assertThat(page.getErrorMessage(), is("Wrong Credentials!"));
    }

}

Information:

Current Version

Clone this wiki locally