Hello,
In my team we're doing cross platform UI testing using Appium and the Appium Java-Client.
The current structure of our project is something like:
mobile
pages
SignInPage
steps
SignInSteps
The steps are "glued" together using Cucuember.
SignInPage looks something like this:
public class SignInPage {
public SignInPage(AppiumDriver driver) {
PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this);
}
// region Identifiers
final String IOS_USERNAME_FIELD = "SignInUsernameField";
final String ANDROID_USERNAME_FIELD = "new UiSelector().resourceIdMatches(\".*id/username.*\")";
final String IOS_PASSWORD_FIELD = "SignInPasswordField";
final String ANDROID_PASSWORD_FIELD = "new UiSelector().resourceIdMatches(\".*id/password_editText.*\")";
final String IOS_SIGN_IN_BUTTON = "SignInButton";
final String ANDROID_SIGN_IN_BUTTON = "new UiSelector().resourceIdMatches(\".*id/signInButton.*\")";
// endregion
@iOSFindBy(accessibility = IOS_USERNAME_FIELD)
@AndroidFindBy(uiAutomator = ANDROID_USERNAME_FIELD)
private MobileElement usernameField;
@iOSFindBy(accessibility = IOS_PASSWORD_FIELD)
@AndroidFindBy(uiAutomator = ANDROID_PASSWORD_FIELD)
private MobileElement passwordField;
@iOSFindBy(accessibility = IOS_SIGN_IN_BUTTON)
@AndroidFindBy(uiAutomator = ANDROID_SIGN_IN_BUTTON)
private MobileElement signInButton;
public MobileElement getUsernameField() {
return usernameField;
}
public MobileElement getPasswordField() {
return passwordField;
}
public MobileElement getSignInButton() {
return signInButton;
}
public void tapUsernameField() {
getUsernameField().click();
}
public void tapSignInButton() {
getSignInButton().click();
}
public void clearUsernameEditText() {
getUsernameField().clear();
}
}
We're not sure in terms of performance and elements lookup where is it best to create an instance of the SignInPage. Currently we have a @Before method in our SignInSteps
that is executed before each Gherkin scenario starts (which is not ideal) but it helps us having a SignInPage
property in the SignInSteps
class that is reused by all the steps.
public class SignInSteps {
private SignInPage signInPage;
AppiumDriver driver;
@Before()
public void setUp() throws MalformedURLException {
driver = TestBase.getInstance().getDriver();
signInPage = new SignInPage(driver);
}
@Given("I fill in the username and password")
public void fill_username_and_password() throws Throwable {
signInPage.tapUsernameField();
signInPage.clearUsernameEditText();
fillEditText(signInPage.getUsernameField(), PropertiesManager.getInstance().getValueForKey(Constants.SIGN_IN_USERNAME));
fillEditText(signInPage.getPasswordField(), PropertiesManager.getInstance().getValueForKey(Constants.SIGN_IN_PASSWORD));
}
// Other sign in steps below
}
However I feel that a cleaner approach would be to create the SignInPage
as a local variable inside each step method in SignInSteps
. Is there any performance impact in creating the page(s) you need in each step?
Also, it's not clear to me, with our current approach (the @Before approach) why exactly does it work even when you create a page for some steps that will be executed later on (so the screen is not even visible at this point).
So maybe the larger question would be how are the elements looked up? Is it when calling PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this);
or when actually accessing the annotated properties (which would be some kind of lazy initialization approach that from my knowledge Java doesn't have, unless my understanding of Java annotations is wrong).
Sorry for the long post, but these are some things that I want to understand thoroughly. So any help is highly appreciated.
Thank you!