【问题标题】:Get sub-item's XPath using Selenium - Java使用 Selenium - Java 获取子项的 XPath
【发布时间】:2017-10-11 14:35:41
【问题描述】:

我在尝试从菜单中单击子项时遇到问题,在第三步中......

  1. 我登录了

  2. 进入项目

  3. 仅删除已创建的卡片 我认为问题在于获得正确的 xpath 页

    打包页面对象;
    导入 org.openqa.selenium.By; 导入 org.openqa.selenium.Keys; 导入 org.openqa.selenium.WebDriver; 导入 org.openqa.selenium.WebElement; 导入 org.openqa.selenium.interactions.Actions;
    公共类页面{ protected final WebDriver webDriver;

    public Page( final WebDriver driver ) {
        this.webDriver =driver;
    }
    
    protected WebElement element(By by ) {
        return webDriver.findElement( by );
    }
    
    public void pressEnter(){
        Actions builder = new Actions(webDriver);
        builder.sendKeys(Keys.RETURN).perform();
    }
    

    }

页面对象:卡片页面

package Helpers;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
public class public class CardPage extends PageObject.Page{

    By close = By.xpath("//*[@id='ngdialog5']/div[2]/div/div[2]");
    By menuCard = By.xpath("html/body/div[1]/div/div/div/div/ui-view/project/div[1]/board/div/div/div/backlog-list/div[2]/div/ul/li/ul/div[1]/card/li/div/div[1]/a[3]");
    By container1stCard = By.xpath("html/body/div[1]/div/div/div/div/ui-view/project/div[1]/board/div/div/div/backlog-list/div[2]/div/ul/li/ul/div/card/li/div");
    By delete3 = By.xpath("//*[contains(text(), 'delete')]");

    By accceptWarning = By.xpath("html/body/div[4]/div/div[10]/button[1]");
    By openCard = By.xpath("//*[@id=\"scrollable\"]/div/backlog-list/div[2]/div/ul/li/ul/div/card/li/div");

    private Services services;
    public CardPage(WebDriver driver) {
        super(driver);
    }
    //SERVICES CARDS
    private void closeCard() {
        element(close).click();
    }
    public void delete1stCard(){
        WebElement Wcontainer1stCard = element(container1stCard);//Menu
        Actions builder = new Actions(super.webDriver);
        Actions hoverOverContainer = builder.moveToElement(Wcontainer1stCard);
        hoverOverContainer.perform();
        Services.waitMilisegundos(1000);
        element(menuCard).click();

        Services.waitMilisegundos(500);
        WebElement deleteBtn = element(delete3);
        deleteBtn.click();//Menu Item
        Services.waitMilisegundos(1000);
    }
    public void openCard(){
        element(openCard).click();
    }
}

页面对象服务

package Helpers;

import PageObject.*;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.*;
import org.openqa.selenium.interactions.Actions;

import java.io.File;
import java.io.IOException;
import java.util.Date;

public class Services {
    WebDriver webDriver;
    private LandingPage landingPage;
    private MainPage mainPage;
    private LoginPage loginPage;
    private ProjectPage projectPage;
    private CardPage cardPage;
    String url;
    ////
    public Services(TmpEnvironment environment) {
        webDriver = new StartDriver( environment.url ).getWebDriver();
    }
    public Services() {
        webDriver = new StartDriver(TmpEnvironment.STAGING).getWebDriver();
    }

    ///LOGIN SERVICES
    public Services login(String email, String passw){
        getLandingPage().goToLoginPage();
        getLoginPage().writeUserName(email)
                .writePassword(passw)
                .clickOnLogin();
        return this;
    }
    public Services enterToProject() {
        getMainPage().enterToProject();
        return this;
    }
    //PAGES GETTERS
    public WebDriver getWebDriver() {
        return webDriver;
    }
    public MainPage getMainPage() {
        if (mainPage == null)
            mainPage = new MainPage(webDriver);
        return mainPage;
    }
    public LoginPage getLoginPage() {
        if (loginPage == null)
            loginPage = new LoginPage(webDriver);
        return loginPage;
    }
    public ProjectPage getProjectPage(){
        if (projectPage == null)
            projectPage = new  ProjectPage(webDriver);
        return projectPage;
    }
    public LandingPage getLandingPage() {
        if (landingPage == null)
            landingPage =  new LandingPage(webDriver);
        return landingPage;
    }
    public CardPage getCardPage() {
        if (cardPage == null)
            cardPage = new CardPage(webDriver);
        return cardPage;
    }
    //CARDS SERVICES
    public Services delete1stCard() {
        getCardPage().delete1stCard();
        return this;
    }
    public static void waitMilisegundos(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void pressEnter(){
        Actions builder = new Actions(webDriver);
        builder.sendKeys(Keys.RETURN).perform();
    }
    public void pressEsc(){
        Actions builder = new Actions(webDriver);
        builder.sendKeys(Keys.ESCAPE).perform();
    }
    //
    public Services goToLoginPage() {
        getLandingPage().goToLoginPage();
        return this;
    }

    public enum TmpEnvironment {
        DEVELOPMENT( "http://tmp-landing-dev.theamalgama.com/index" ),
        STAGING( "https://tmpapp.theamalgama.com" ),
        PRODUCTION( "https://tmpapp.com" );
        public final String url;
        TmpEnvironment( final String url ) {
            this.url = url;
        }
        public String getUrl(){
            return url;
        }
    }
}

测试类

package z_Test;

import Helpers.Services;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;

import static Helpers.Services.waitMilisegundos;

public class CardTest {
    static String email, password;
    Services services;

    public CardTest(){
        services = new Services();
    }
    @BeforeClass
    public static void setData(){
        email = "test01@testing.com";
        password = "password";

    }
    @After
    public void CloseDriver(){
        services.getWebDriver().close();
    }


    /////////////////////////////////////////////////////////////
    @Test
    public void cardTest(){ 
        services.login(email,password);
        services.enterToProject()
        .delete1stCard();
        waitMilisegundos();
    }
}

然后是尝试删除卡时的错误消息。

[org.openqa.selenium.ElementNotVisibleException:元素不可见
(会话信息:chrome=58.0.3029.110)(驱动程序信息: 铬驱动程序=2.30.477691 (6ee44a7247c639c0703f291d320bdf05c1531b57),平台=Linux 4.4.0-79-generic x86_64)(警告:服务器未提供任何堆栈跟踪信息)命令持续时间或超时:96 毫秒 构建信息:版本:“未知”,修订:“未知”,时间:“未知” 系统信息:主机:'julieta',ip:'127.0.1.1',os.name:'Linux', os.arch:'amd64',os.version:'4.4.0-79-generic',java.version: '1.8.0_131' 驱动程序信息:org.openqa.selenium.chrome.ChromeDriver 功能 [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false,networkConnectionEnabled=false, 铬={铬驱动程序版本=2.30.477691 (6ee44a7247c639c0703f291d320bdf05c1531b57), userDataDir=/tmp/.org.chromium.Chromium.rQuYOu}, 需要HeapSnapshot=true,pageLoadStrategy=正常, databaseEnabled=false,handlesAlerts=true,hasTouchScreen=false, 版本=58.0.3029.110,平台=LINUX,browserConnectionEnabled=false, nativeEvents=true,acceptSslCerts=true,locationContextEnabled=true, webStorageEnabled=true,browserName=chrome,takeScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true, unexpectedAlertBehaviour=}] 会话 ID: a33c710a9bae046f731a66bb56b344b8 在 sun.reflect.NativeConstructorAccessorImpl.newInstance0(本机方法) 在 sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 在 sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 在 java.lang.reflect.Constructor.newInstance(Constructor.java:423) 在 org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:215) 在 org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:167) 在 org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:671) 在 org.openqa.selenium.remote.RemoteWebElement.execute(RemoteWebElement.java:272) 在 org.openqa.selenium.remote.RemoteWebElement.click(RemoteWebElement.java:82) 在 PageObject.CardPage.delete1stCard(CardPage.java:92) 在 Helpers.Services.delete1stCard(Services.java:201) 在 z_Test.CardTest.cardTest(CardTest.java:69) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:498) 在 org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) 在 org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 在 org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) 在 org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 在 org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) 在 org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) 在 org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) 在 org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) 在 org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 在 org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 在 org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 在 org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 在 org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 在 org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) 在 org.junit.runners.ParentRunner.run(ParentRunner.java:363) 在 org.junit.runner.JUnitCore.run(JUnitCore.java:137) 在 com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) 在 com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) 在 com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) 在 com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

]

我也尝试使用不同的 xpath,所以当我运行它时,我得到了另一种例外:org.openqa.selenium.NoSuchElementException: Unable to locate element:

【问题讨论】:

  • 你的代码是什么?你说我为什么失败是什么意思?你从来没有提到你做了什么以及它到底是如何失败的
  • 是的,对不起!我现在就编辑我的帖子
  • 您正在执行 movetoelement 的操作,但没有使用相同的操作本身进行点击?您应该将这些链接在一起,menuCard 和 deleteBtn click
  • @TarunLalwani 谢谢。首先我打开菜单点击元素(menuCard).click();然后我从菜单中的选项中选择一个: element(delete3).click();//Menu Item 这个命令很好用,因为我已经在与菜单的其他交互中使用它并且很好用。我认为问题是xpath。我将其发布为“元素”方法以使我的代码更清晰。 protected WebElement element(By by ) { return webDriver.findElement( by ); }
  • 请用英文写出你的测试步骤是什么。登录后我很难理解您的意图。您的代码引用了许多不在您提供的代码中的变量。请使用minimal reproducible example 更新您的代码。

标签: java selenium xpath subitem


【解决方案1】:

我认为您有很多问题需要改进。

  1. 您正在使用绝对 XPath,这始终是一种不好的做法。它们非常脆弱。您应该花一些时间阅读一些关于手工制作 XPath 的教程。它们会更短,更容易阅读,但也不会那么脆弱。

  2. 与其使用方法delete1stCard(),不如创建一个方法deleteCard(int index),该方法采用索引,以便您可以删除所需的任何卡。您可能需要另一种方法deleteCard(String cardName) 接受卡名并将其删除。我认为这可能比按索引删除更有用,但我不知道您的测试用例是什么样的。 (或者更好的是,实现一个卡片组件类,如下所示,它可以解决这个问题。)

  3. 你的班级Services 似乎是一个包罗万象的桶。做一个适当的页面对象模型并添加一个处理来自登录页面的登录的登录页面,一个处理该页面的项目页面,等等。一旦您将所有内容重新组织成适当的页面对象,我不知道Services 应该包含什么...我认为您可能不需要它。

  4. 我还建议您创建组件页面对象,例如卡片,一些工具栏。 “页面对象”不必是整个页面。它可以是任何可重用的组件。您可以将Services 类中的一些可用功能移动到TimerMenuProjectBar 等组件中,以表示页面顶部的不同工具栏及其功能。

  5. 您将拥有一个代表卡片页面的 CardsPage 页面对象,例如https://tmpapp.theamalgama.com/#/projects/681/cards,但您也会有一个 CardComponent 代表页面上的实际卡片,这些内容包含在 <card> 标记中。 CardComponent 将包含单张卡的功能...名称、该卡的播放按钮、该卡的完成图标、该卡的删除/编辑/等选项等等。

现在来看实际答案。正如我建议你写的那样,我写了一个CardComponent 类。你传入卡片的名称,它就会得到它的句柄。从那里,您只需致电card.delete()card.whatever(),该操作将在您指定的卡上完成。它使处理个人卡片、项目等变得更加容易。更新卡片的功能很容易...更改在 CardComponent 页面对象等中。

public class CardComponent
{
    private WebDriver driver;
    private WebElement card;
    private By deleteCardLocator = By.xpath(".//p[@class='option-list-option'][.='Delete']");
    private By acceptDeleteButtonLocator = By.xpath("//button[.='Accept']");

    public CardComponent(WebDriver webDriver, String cardName)
    {
        driver = webDriver;
        card = new WebDriverWait(webDriver, 10)
                .until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//card[contains(., '" + cardName + "')]")));
    }

    public void delete()
    {
        openMenu();
        card.findElement(deleteCardLocator).click();
        driver.findElement(acceptDeleteButtonLocator).click();
    }

    private void openMenu()
    {
        new Actions(driver).moveToElement(card).perform();
        card.findElement(By.id("dots")).click();
    }
}

要使用此代码,请导航到卡片页面并调用

CardComponent card = new CardComponent(driver, "card to delete");
card.delete();

我创建了几张新卡片并测试了这段代码,它可以工作。

【讨论】:

  • JeffC,非常感谢您的所有建议!我会仔细阅读并改进代码。谢谢你的时间。我有一个问题,请您向我推荐任何教程、视频等来学习如何制作手工 XPaths,好吗?我在谷歌搜索过,但我没有找到关于如何构建非绝对 xpaths 的任何信息
  • 我不能说我认为有什么特别好的,但大多数人都在谈论手工制作 XPath,例如guru99.com/xpath-selenium.html。我学到的大部分知识都是通过阅读 SO 问题和答案以及在谷歌上搜索的。
猜你喜欢
  • 1970-01-01
  • 2012-09-29
  • 2014-03-01
  • 2012-07-22
  • 1970-01-01
  • 2020-09-04
  • 2015-08-05
  • 2019-04-03
  • 1970-01-01
相关资源
最近更新 更多