所以我为FindElement
和FindElements
创建了2个通用函数:
public class Find
{
public static IWebElement Element(IWebDriver driver, Func<IWebDriver, IWebElement> expectedCondtions,
By locator, IWebElement finder = null, int timeOutInSeconds = 120)
{
WebDriverWait webDriverWait = CreateWebDriverWait(driver, timeOutInSeconds);
webDriverWait.Until(expectedCondtions);
if (finder != null)
return finder.FindElement(locator);
return driver.FindElement(locator);
}
public static ReadOnlyCollection<IWebElement> Elements(IWebDriver driver, Func<IWebDriver, ReadOnlyCollection<IWebElement>> expectedCondtions,
By locator, IWebElement finder = null, int timeOutInSeconds = 120)
{
WebDriverWait webDriverWait = CreateWebDriverWait(driver, timeOutInSeconds);
webDriverWait.Until(expectedCondtions);
if (finder == null)
return driver.FindElements(locator);
return finder.FindElements(locator);
}
private static WebDriverWait CreateWebDriverWait(IWebDriver driver, int timeOutInSeconds)
{
WebDriverWait webDriverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeOutInSeconds));
webDriverWait.IgnoreExceptionTypes(typeof(NoSuchElementException));
return webDriverWait;
}
}
用法:
IWebElement element =
Find.Element(
driver,
ExpectedConditions.ElementIsVisible(By.CssSelector("bla bla")),
By.CssSelector("bla bla"));
正如您所看到的,我将locator
两次发送到我的function
,所以我的问题是有一些方法只发送一次吗?
答案 0 :(得分:1)
如果我理解你的意图,那就是编写一些通用的函数来使代码更清晰。这在概念上是一件好事,但我认为在这种情况下它并没有实现你所希望的。我看到很多人都想做这样的事情。他们希望围绕Selenium提供的简单方法创建一个包装器,但最终他们没有简化他们的代码,他们使它变得更复杂,在调用堆栈中添加了另一层东西,可能引入了bug,并创建了一个专有的任何使用您的代码库的人都必须学习API而不是仅使用基本的Selenium命令。当每个元素查找通过单个函数时,该函数引入的任何错误或间歇性问题都会出现在套件中的每个脚本中。
一个简单的比较:
使用Find
方法
IWebElement e = Find.Element(Driver, ExpectedConditions.ElementToBeClickable(By.CssSelector("#checkboxes > input")), By.CssSelector("#checkboxes > input"), null, 10);
仅限Selenium
IWebElement e = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)).Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("#checkboxes > input")));
最终使用你的方法并不是很清洁。我会继续以Selenium为唯一的方式,并做这样的事情来重用等待,使代码更清洁。
WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10));
IWebElement e = wait.Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("#checkboxes > input")));
IWebElement e2 = wait.Until(ExpectedConditions.ElementToBeClickable(By.Id("checkboxes")));
由于Until()
会返回该元素,因此您可以将.Click()
wait.Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("#checkboxes > input"))).Click();
如果您使用页面对象模型(通常,您应该使用),您将存储定位器,然后使用它们,如下所示,这使代码更清晰。
wait.Until(ExpectedConditions.ElementToBeClickable(checkboxLocator)).Click();
话虽如此,我会像这样重写这些
public class Find
{
public static IWebElement Element(WebDriverWait wait, Func<IWebDriver, IWebElement> expectedCondition)
{
return wait.Until(expectedCondition);
}
public static IReadOnlyCollection<IWebElement> Elements(WebDriverWait wait, Func<IWebDriver, IReadOnlyCollection<IWebElement>> expectedCondition)
{
return wait.Until(expectedCondition);
}
}
Until()
方法返回找到的元素,因此您只需返回返回,这样可以节省两次查找的内容,并且无需两次发送定位器。
我删除了您在方法中创建的WebDriverWait()
,因为您确实应该重用单个实例。我不了解你,但我通常不需要10个不同的等待时间,我可能会使用一对。在测试脚本中声明它们并传递它们。
我删除了finder
元素,因为它不应该被删除。您可以使用查找子元素的CSS选择器轻松创建单个定位器。
在CreateWebDriverWait()
中,您无需忽略NoSuchElementException
,它已经内置。因此,此功能可以简化为
private static WebDriverWait CreateWebDriverWait(IWebDriver driver, int timeOutInSeconds)
{
return new WebDriverWait(driver, TimeSpan.FromSeconds(timeOutInSeconds));
}
在那时你必须问自己为什么Find.CreateWebDriverWait()
比new WebDriverWait()
更好?你通过编写这个单独的方法获得了什么?通过使用单独的方法创建WebDriverWait
,即使超时为10秒,您也每次都创建一个新实例,尤其是Element()
和Elements()
。所以,我从代码中省略了它。