2016-11-28 2 views
0

Dans mon équipe, nous effectuons des tests d'interface utilisateur multi-plateforme en utilisant Appium et le client Java Appium. La structure actuelle de notre projet est quelque chose comme:Appium PageObject - Quand/Où instancier la page?

mobile 
    pages 
    SignInPage 
    steps 
    SignInSteps 

Les étapes sont « collés » ensemble à l'aide Cucuember. SignInPage ressemble à quelque chose comme ceci:

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(); 
    } 
} 

Nous ne sommes pas sûrs en termes de performance et des éléments recherche où est-il préférable de créer une instance de la SignInPage. Actuellement, nous avons une méthode @Before dans nos SignInSteps qui est exécutée avant le début de chaque scénario Gherkin (ce qui n'est pas idéal) mais cela nous aide à avoir une propriété SignInPage dans la classe SignInSteps qui est réutilisée par toutes les étapes.

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 
} 

Cependant, je pense qu'une approche plus propre serait de créer la SignInPage comme une variable locale dans chaque méthode étape SignInSteps. Y a-t-il un impact sur les performances dans la création des pages dont vous avez besoin à chaque étape? De plus, avec notre approche actuelle (l'approche @Before), je ne comprends pas pourquoi exactement cela fonctionne même lorsque vous créez une page pour certaines étapes qui seront exécutées plus tard (donc l'écran n'est même pas visible) À ce point).

Alors peut-être la plus grande question serait de savoir comment les éléments sont recherchés? Est-ce lors de l'appel de PageFactory.initElements (nouveau AppiumFieldDecorator (pilote, 15, TimeUnit.SECONDS), this); ou en accédant réellement aux propriétés annotées (ce qui serait une sorte d'approche d'initialisation paresseuse que, à ma connaissance, Java ne possède pas, à moins que ma compréhension des annotations Java ne soit fausse).

Désolé pour le poste long, mais ce sont des choses que je veux bien comprendre. Donc, toute aide est très appréciée.

Merci!

Répondre

0

j'ai fait quelques recherches (débogage) et je l'ai trouvé la réponse:

Lorsque vous appelez PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this); les propriétés annotées de la page sont définies (décorée) par réflexion (voir AppiumFieldDecorator) avec un proxy (ElementInterceptor) qui enveloppe un MobileElement. Chaque fois que vous appelez une méthode sur la propriété annotée, vous appelez le proxy qui recherche l'élément et transfère l'appel de la méthode. Il n'y a pas de cache entre (contrairement à WidgetInterceptor que je n'ai pas encore compris où il est utilisé). Donc, dans mon cas, créer une page une seule fois ou à chaque étape ne fait pas vraiment de différence car la recherche d'éléments est effectuée chaque fois que vous interagissez avec elle (ce qui est bon, je suppose, mais ça peut avoir une performance impact aussi).

J'ai aussi attaché quelques captures d'écran ci-dessous:

Stacktrace lorsque vous appelez PageFactory.initElements (nouveau AppiumFieldDecorator (pilote, 15, TimeUnit.SECONDS), ceci);

enter image description here

enter image description here

Stacktrace lorsque vous appelez click sur un élément

enter image description here

enter image description here

Hope this helps autres aussi bien comprendre comment fonctionne l'outil.