Reputation: 4430
I was using selenium to get data in a table of the website to analytics. I'm must scan and get a table approximately ~1000 rows.
I know the page has Javascript, which automatically updates the DOM. But the table too many rows, so when I scan my code always throw an exception.
I tried with this code:
Boolean breakIt = true;
List<IWebElement> result = new List<IWebElement>();
while (true)
{
breakIt = true;
try
{
IWebElement baseTable = browser.FindElementById("column2");
ReadOnlyCollection<IWebElement> rowsTable = baseTable.FindElements(By.XPath("id('oTable')/table/tbody/tr"));
foreach (IWebElement rows in rowsTable) {
if (rows.FindElements(By.XPath("td")).Count == 10)
result.Add(rows);
}
if (breakIt)
{
break;
}
}
catch (StaleElementReferenceException ser)
{
if (ser.Message.Contains("element is not attached"))
{
browser.Refresh();
browser.WaitForPageToLoad();
browser.GoToFrame(browser.FindElementByXPath("//*[@id='form1']/div[3]/iframe"));
breakIt = false;
}
}
}
It throws an exception like:
Stale element reference: element is not attached to the page document.
Have any idea to resolve my problem? I think using multiple Thread
is the best method.
But I tried with multiple Thread
it also return exceptions.
I think after getting rowsTable.Count
. Divide this /2. And create two thread run this?
Upvotes: 4
Views: 22892
Reputation: 28751
I am in a similar situation. My list is not that big, so some of the ideas I will present are not applicable to my case and consequently I haven't tested them.
Assuming the table is updated periodically, then if the first table scan fails on StaleElementReferenceException
, second table scan starts right at the beginning of the next quiet period and has a good chance to succeed before next update happens. Provided that you can finish scan faster than is the update period.
waitForAngular()
is a method from https://stackoverflow.com/a/30540634/6081394 or from https://stackoverflow.com/a/38657507/6081394, or use both one after another to be extra sure ;)
var finished = false;
for (var i = 0; i < 10; i++)
{
try
{
waitForAngular()
// scan table here
finished = true;
break;
}
catch (StaleElementReferenceException e)
{
continue;
}
}
if (!finished)
{
// test flaked out
}
I am doing this myself.
Local selenium is faster than Remote, so using the previous approach, there is much higher chance you can finish the table scan before page updates. Not realistic, I know, but an option nonetheless.
If the table is just refreshing but the data (and number of rows) are the same, you can first count rows in the table, then go and check 10 rows at a time, retrying in case of StaleElementReferenceException
like before.
You can query for row ranges with CSS, see https://stackoverflow.com/a/28061560/6081394
tr:nth-child(n+2):nth-child(-n+4)
JavaScript execution is event driven and single-threaded. That guarantees that when your injected JavaScript is running, the script updating the page is not. What might happen, though, is that the script gets executed when the table was in the middle of an update. You have to detect that, bail out, run the script again and hope next time will go better. See https://stackoverflow.com/a/6285793/6081394 for an example.
This is the way I would pursue if I was faced with a large table and fast updates.
AFAIK, selenium drivers end up executing JavaScript in pages to do their work, and that execution is single threaded, because all JavaScript execution in a single browser tab is single-threaded. So any multi threaded C# test you write will end up executing the Selenium operations sequentially in a serialized order. So multi threading is not a solution.
Upvotes: 6