在多个页面上具有相同By定位符的元素的问题

本文关键字:By 定位 元素 问题 | 更新日期: 2023-09-27 18:17:03

我有5页(A, B, C, D, E). A, B, C和D每个包含一个按钮Id 'Next'将加载下一页。现在,问题是,有时(不是总是),"下一步"按钮不点击每个页面。

我的代码应该做以下事情:我们启动浏览器并通过URL导航到页面A。然后,我们在页面上执行一些逻辑,然后单击"下一步"按钮。我们到达页面B,在页面上执行一些逻辑,然后单击"下一步"按钮。我们到达C页,立即点击"下一步"按钮,没有做任何其他事情。在D页,我们执行一些逻辑并单击"Next"按钮。(以此类推…)

现在,问题是,在C页,"下一步"按钮并不总是点击,但它不会抛出FindElement错误。所以它试图在D页执行逻辑,而web驱动程序崩溃,因为它仍然在c页上。我怎么能解决这个问题?请注意,我确实使用动态webdriver等待元素出现在页面上,但这使得如此不同,因为相同的定位器它总是相同的(ID = 'Next')。另外,请注意,我没有返回我的PageObjects -我不确定这是否是绝对需要的。

任何想法吗?

下面是我的代码:
   public class Page
    {      
        public Page()
        {
            PageFactory.InitElements(SeleniumTests.driver, this);
        }       
    }
public static class SeleniumTests
    {
        public static IWebDriver driver { get; set; }
    }
 class Page_1 : Page
    {       
        [FindsBy(How = How.Id, Using = "Next")]
        public void Continue()
        {
            btnNext.SafeClick();
        }
    }
class Page_2 : Page
        {       
            [FindsBy(How = How.Id, Using = "Next")]
            public void Continue()
            {
                btnNext.SafeClick();
            }
        }
class Page_3 : Page
        {       
            [FindsBy(How = How.Id, Using = "Next")]
            public void Continue()
            {
                btnNext.SafeClick();
            }
        }
  class Page_4 : Page
            {       
                [FindsBy(How = How.Id, Using = "Next")]
                public void Continue()
                {
                    btnNext.SafeClick();
                }
            }
  class Page_5 : Page
            {       
                [FindsBy(How = How.Id, Using = "Next")]
                public void Continue()
                {
                    btnNext.SafeClick();
                }
            }
   public static class SeleniumExtensionMethod
    { 
        public static WebDriverWait wait = new WebDriverWait(SeleniumTests.driver, TimeSpan.FromSeconds(15));
        public static void SafeClick(this IWebElement webElement, Double seconds = 15)
            {
                try
                {
                    wait.Until(ExpectedConditions.ElementToBeClickable(webElement)).Click();
                }
                catch (System.Reflection.TargetInvocationException ex)
                {
                    Console.WriteLine(ex.InnerException);
                }
            }
    }

最后:

 public partial class Form1 : Form
    {
        Page_1 pageA = new Page_1();
        pageA.PerformSomeLogic();
        pageA.Continue();
        Page_2 pageB = new Page_2();
        pageB.PerformSomeLogic();
        pageB.Continue();
        Page_3 pageC = new Page_3();
        // Don't do anything here, just continue.
        pageC.Continue();

        Page_4 pageD = new Page_4();
        pageD.PerformSomeLogic();// -----> here is crashes, as the previous line 'pageC.Continue()' was not really executed, it seems as though the button was clicked 2 times on page B
        pageD.Continue() ;          

        Page_5 pageE = new Page_5();
        pageD.PerformSomeLogic();
        pageE.Continue();   
}

编辑:我想要的,理想情况下是做一些动态等待,这实际上会在这种情况下工作。我也可以使用Thread.Sleep();这解决了我的问题,但这是一个代码气味,我想避免它

在多个页面上具有相同By定位符的元素的问题

你有两个选择:

  1. 确保页面已加载并且您在该页面
    您可以为页面的唯一元素添加wait.Until()

  2. 获取基于父页面的按钮选择器
    由于您在每个页面对象中都有Continue(),因此您可以使用css选择器来根据父页面或基于页面中不存在的唯一部分来识别按钮,否则在每个页面中使用相同选择器的相同方法有什么意义?您可以通过使用FireBug轻松地找到它。只需导航到页面,右键单击该元素,单击FireBug检查元素,然后右键单击该元素并单击"复制CSS路径"。

例如

:
假设有一个divid='pageC'
你可以使用像#pageC #Next

这样的css选择器

您的代码对于您要完成的任务来说过于复杂了。

首先得到你的IWebDriver:

IWebDriver driver = new FirefoxDriver();

你应该在这个场景中使用隐式等待,让我们从超时设置为15秒开始。这将确保driver.FindElements()调用在超时前搜索15秒。如果在15秒结束之前找到元素,它将在那一刻停止等待。

driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));

由于您的下一个按钮在每个页面上都具有相同的id,因此我们可以使用By.Id("Next")来查找每个页面上的下一个按钮。

driver.FindElement(By.Id("Next"));

把它们放在一起:

IWebDriver driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));
for(int page = 1; page < 5; page++) //Iterate through all pages, click next when applicable
{
    driver.FindElement(By.Id("Next"));
}

IWebDriver driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));
if(driver.FindElement(By.Id("Next")).Enabled) //Ensure button is clickable before proceeding
{
    driver.FindElement(By.Id("Next"));
}

我猜您的脚本运行得足够快,在第3页有机会加载之前,它在第2页上第二次单击Next按钮。解决这个问题的一种方法是等待Next按钮失效。基本逻辑是

1. Click the Next button
2. Wait for the Next button to be stale
代码可能看起来像
public void Continue()
{
    btnNext.SafeClick();
    btnNext.WaitForStale();
}

其中.WaitForStale()只是

wait.until(ExpectedConditions.stalenessOf(btnNext));

另外,除非你真的简化了你的Page Objects,否则似乎没有理由要从Page_1到Page_5。如果它们都有相同的功能,它们应该放在一个页面中,每次移动到下一页时,您只需重新实例化页面,例如

public partial class Form1 : Form
{
    Page_1 pageA = new Page_1();
    pageA.PerformSomeLogic();
    pageA.Continue();
    pageB = new Page_1(); <-- this can be Page_1() or better yet, renamed to ContentPage or something that better represents the type of page rather than the page number.
    pageB.PerformSomeLogic();
    pageB.Continue();