问题
I have such a trivial issue but I am having a hard time getting my code to properly wait for an object before moving on.
I have the following config set for my driver
session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(60);
I was expecting this to mean that it would wait a minimum of 60 seconds before throwing errors related to element identification such as
Message: System.InvalidOperationException : An element could not be located on the page using the given search parameters.
However this is not the case. I get the error around 2 seconds in when trying to call the following command.
WindowsElement btn = session.FindElementByXPath("//Button[@Name='NEXT']");
btn.Click();
The error gets thrown on the line where I am just defining the buttons properties and not on the actual Click() method. Am I not passing the elements properties correctly? Why would the instantiation of the button also do the searching of it?
回答1:
There it a open issue on the winappdriver github. Take a look at this comment about it. It seems to be a Appium issue. I have no idea about the status of this issue.
Basically, this means you will have to resort to a workaround. Using Thread.Sleep(/*milliseconds*/)
is a bad idea.
I implemented a while
loop in a function to get a control by Automation ID like this:
/// <summary>
/// Gets a UI element based on a AutomationId.
/// </summary>
/// <param name="automationId">The AutomationId is a unique value that can be found with UI inspector tools.</param>
/// <param name="controlName">The name of the UI element.</param>
/// <param name="timeOut">TimeOut in milliseconds</param>
/// <returns></returns>
protected WindowsElement GetElement(string automationId, string controlName, int timeOut = 10000)
{
bool iterate = true;
WindowsElement control = null;
_elementTimeOut = TimeSpan.FromMilliseconds(timeOut);
timer.Start();
while (timer.Elapsed <= _elementTimeOut && iterate == true)
{
try
{
control = Driver.FindElementByAccessibilityId(automationId);
iterate = false;
}
catch (WebDriverException ex)
{
LogSearchError(ex, automationId, controlName);
}
}
timer.Stop();
Assert.IsFalse(timer.Elapsed > _elementTimeOut, "Timeout Elapsed, element not found.");
timer.Reset();
return control;
}
Using a loop has some advantages versus Thread.Sleep()
, it's more flexible and you have much more options than simply blocking the code execution.
A few of the advantages:
- Your test script keeps executing: Imagine your script pausing for 5 seconds while the application under test keeps going. A lot could happen in those 5 seconds your script might want to know about. But it can't, because the code execution is blocked if you use 'Thread.Sleep()'.
- Dynamic wait: A while loop will iterate until the condition is satisfied. This makes your script continue with the test as soon as this condition is met, thus making your script run faster. E.g. you are waiting for a page to load.
Thread.Sleep(5000)
will assume it's OK to continue, while the loop knows it's OK to continue the test. - With the timer/time out combo, you can check how long the operation took (e.g. save some edits) and if it took longer than the timeout, you know it's not OK to continue.
Alternately, this code will work just as well:
protected WindowsElement GetElement(string automationId, string propertyName, int timeOut = 10000)
{
WindowsElement element = null;
var wait = new DefaultWait<WindowsDriver<WindowsElement>>(Driver)
{
Timeout = TimeSpan.FromMilliseconds(timeOut),
Message = $"Element with automationId \"{automationId}\" not found."
};
wait.IgnoreExceptionTypes(typeof(WebDriverException));
try
{
wait.Until(Driver =>
{
element = Driver.FindElementByAccessibilityId(automationId);
return element != null;
});
}
catch(WebDriverTimeoutException ex)
{
LogSearchError(ex, automationId, propertyName);
Assert.Fail(ex.Message);
}
return element;
}
Above code will only throw a WebDriverTimeoutException
instead of continuously throwing NoSuchElementException
. It does not use a while loop, but I suspect wait.Until(...)
is doing something similar, since WinAppDriver polls the gui every 500ms (see the PollingInterval
property on the DefaultWait
object.
来源:https://stackoverflow.com/questions/56117645/waiting-for-element-before-clicking-with-winappdriver