Schlagwörter

, , ,

In meinem letzten Post habe ich ein paar Ideen für ein Framwork zum Erstellen von vollständig deklarativen PageObjekten zusammengestellt. Dieses Framework erlaubt es über annotierte Interfaces zu beschreiben aus welchen Elementen eine Seite besteht und erstellt daraus ein fertig implementiertes PageObjekt. Seither hat mich eine Diskussion mit einem Kollegen umhergetrieben, der behauptet ein PageObjekt müsse mehr tun als nur Zugriffe auf Elemente bereitstellen, sondern höherwerige Funktionen zur Verfügung stellen, die den Seitenaufbau in fachliche Funktionen kapseln.

Auch wenn ich nicht 100% seiner Meinung bin, ich halte es für durchaus vertretbar in einem Pageobjekt ein


index.loginLink().click();

zu haben, anstatt einem


index.gotoLogin();

So stimmt es doch, dass es an manchen Stellen sinnvoll ist höherwertige Funktionen anzubieten. Diese höherwertigen Funktionen sind der Punkt, an dem es keinen Sinn mehr macht mit Deklaration weiterzuarbeiten und Implementierung ins Spiel kommen sollte. Das Framework kann sowas wie folgt abbilden

@Page(name="Login")
public abstract class Login {
  @PageAccessor(uri="login.html")
  public abstract void open();

  public void doLogin(String username, String password) {
    usernameTextbox().type(username);
    passwordTextbox().type(password);
    submitLoginButton().click();
  }

  @Locator(name="Username", xpath="//input[@name='username']")
  public abstract ITextBox usernameTextbox();

  @Locator(name="Password", xpath="//input[@name='password']")
  public abstract ITextBox passwordTextbox();

  @Locator(name="Login Submit", cssSelector=".submitLoginButton")
  public abstract IButton submitLoginButton();
}

Je nachdem wie streng man es mit dem Ansatz nimmt, dass PageObjekte nur höherwertige Funktionen nach außen geben dürfen, kann man die Locator-Methoden public oder package-private machen.

Daneben kann man Locator-Methoden von höherwertigen Methoden durch eine Vererbungshierarchie trennen, z.. zwei-stufige Vererbungen sind dabei denkbar:

@Page(name="Login")
public abstract class LoginPO implements LoginLocator {
  public void doLogin(String username, String password) {
    usernameTextbox().type(username);
    passwordTextbox().type(password);
    submitLoginButton().click();
  }
}

public interface LoginLocator {
  @PageAccessor(uri="login.html")
  void open();

  @Locator(name="Username", xpath="//input[@name='username']")
  ITextBox usernameTextbox();

  @Locator(name="Password", xpath="//input[@name='password']")
  ITextBox passwordTextbox();

  @Locator(name="Login Submit", cssSelector=".submitLoginButton")
  IButton submitLoginButton();
}

Die Pedanten (ja, ich meine dich, Nils ;)) können aus dem Locator-Interface auch eine abstrakte Klasse machen, damit die Locator-Methoden package-private werden so ja keine „minderwertigen“ Funktionen nach außen gegeben werden.

P.S.

Ich habe zu diesem Framework ein Opensource-Projekt gestartet. Die in diesem und letzten Post beschriebenen Funktionen sind dort bereits implementiert: https://sourceforge.net/projects/popperfw/

Advertisements