Using WebDriverWait to wait if value changes after some time - c#

I'm using WebDriverWait to get the value of a WebElement after some time, but if it doesn't change, I don't want it to fail, just get the current value and keep going with the execution.
I'm running a process in a Web that has a "process completed" label that I use to check if it finished. On the same screen there is an "elapsed time" for the process that I need to read to report how much time it took to run the process.
The issue is that there is a backend process running even after the "process completed" label appeared, and the time gets updated. I can't know how much time this "backend update" will take, and I don't know if in happy runs it can appear before mentioned label, for the moment, from my tests it goes from 10 to 40 seconds (60 to be sure).
We have a Waits class for this kind of stuff, but we didn't have a method for this text change validation, so I came up with this:
private static WebDriverWait _customWait(int value) => new WebDriverWait(
clock,
Driver.Instance,
TimeSpan.FromSeconds(value),
TimeSpan.FromMilliseconds(Settings.Timeouts.SleepIntervalInMillis));
public static void WaitForTextElementToChange(Func<IWebDriver, IWebElement> elementFinder, IWebDriver driver, int time)
{
string oldValue = elementFinder.Invoke(driver).Read();
_customWait(time).Until(drv =>
{
try
{
return !elementFinder.Invoke(driver).Read().Contains(oldValue);
}
catch (NotFoundException)
{
return true;
}
catch (StaleElementReferenceException)
{
return true;
}
});
}
This works. And I remark it because I don't fully understand yet the syntax and logic behind that Until method, I know that it gives a WebDriverTimeoutException and I left it that way to be an additional method for the framework.
So, if the value changes it gets out and keeps running lovely, but in this particular case, if it doesn't change, I don't need it to throw that exception so I called it within a try/catch.
Waits.WaitForProcessToFinish(drv => processCompleteLabel); //<- context code
try
{
//If value changes before 60 secs everything is fine
Waits.WaitForTextElementToChange(drv => timeElement, someDriverObject, 60);
//60 is a hardcoded int value just for testing
}
catch (WebDriverTimeoutException)
{
//But if it doesn't change just do nothing
}
string timeElapsed = timeElement.Read(); //<- context code
My question is, would it be ok to leave it that way?
What would you do instead?

Based on what I see in the source I don't think there is a way around this with how Until works.
The Until method executes a method passed to it in a loop until either a bool or object is returned or the timeout occurs.
public virtual TResult Until<TResult>(Func<T, TResult> condition)
{
if (condition == null)
{
throw new ArgumentNullException("condition", "condition cannot be null");
}
var resultType = typeof(TResult);
if ((resultType.IsValueType && resultType != typeof(bool)) || !typeof(object).IsAssignableFrom(resultType))
{
throw new ArgumentException("Can only wait on an object or boolean response, tried to use type: " + resultType.ToString(), "condition");
}
Exception lastException = null;
var endTime = this.clock.LaterBy(this.timeout);
while (true)
{
try
{
var result = condition(this.input);
if (resultType == typeof(bool))
{
var boolResult = result as bool?;
if (boolResult.HasValue && boolResult.Value)
{
return result;
}
}
else
{
if (result != null)
{
return result;
}
}
}
catch (Exception ex)
{
if (!this.IsIgnoredException(ex))
{
throw;
}
lastException = ex;
}
// Check the timeout after evaluating the function to ensure conditions
// with a zero timeout can succeed.
if (!this.clock.IsNowBefore(endTime))
{
string timeoutMessage = string.Format(CultureInfo.InvariantCulture, "Timed out after {0} seconds", this.timeout.TotalSeconds);
if (!string.IsNullOrEmpty(this.message))
{
timeoutMessage += ": " + this.message;
}
this.ThrowTimeoutException(timeoutMessage, lastException);
}
Thread.Sleep(this.sleepInterval);
}
}
What you're doing works, But I would suggest having your own timeout loop without using Wait.Until() since it's core function is to throw an exception if there is an issue.

Related

How to Call a method from a Class Library Variably

I am trying to find a solution that allows me to write one method in my forms project that can variably call multiple different methods from my class library project.
The reason for this being that I want to implement retry logic around these methods and prevent myself from repeating it for each different variety of method. The only consistent thing about the class library methods are that they all return Task<bool> so its easy to await on and perform logic with.
So far I have the following:
public async Task Runner(string methodName, params object[] parameters)
{
ThreadTimer.Start();
var tries = 0;
var ok = false;
while (tries <= 180)
{
try
{
var parameterTypes = (from p in parameters select p.GetType()).ToArray();
var mi = typeof(string).GetMethod(methodName, parameterTypes); //Currently returns null
var result = (Task<bool>)mi.Invoke(null, parameters);
ok = await result;
if (ok) break;
}
catch (Exception ex)
{
if (ex.InnerException == null)
{
ExceptionLabel2.Text = ex.Message;
}
else
{
ExceptionLabel1.Text = ex.Message;
ExceptionLabel2.Text = ex.InnerException.Message;
}
}
finally
{
tries++;
}
}
if (ok)
{
ThreadTimer.Dispose();
}
else
{
CShellControls.ExitWindowsEx(0, 0); //Logoff
}
}
The idea behind this is to declare a method name in a string and pass an array of parameters with it. I then used .GetMethod() to try and fetch the desired method info but unfortunately this returns null.
I have tried a few different methods but I'm open to suggestions and critique. As far as optimizing code goes I haven't really thought much into it, I just want to try and get this working first before approaching a more efficient method.
Thank you in advance!

Async/Await with throw statement ends in weird behavior

I have a method:
public async Task TakeOrder(int orderId)
{
var request = new Request() { OrderId = orderId, UserId = _myUserId };
var result = await _service.Request<OrderRequest, OrderResponse>(request);
if (!result.Body.Success) // guaranteed to be true
{
if (result.Body.Error != null)
{
throw result.Body.Error;
}
else
{
throw new Exception("Failed to take order"); // always gets hit
}
}
}
So whenever I step into it, the code runs fine until the first if statement, no matter that result.Body.Success is true, it jumps right after into throw new Exception("Failed to take order") statement. Any ideas?
P.S.
I am more interested in why this works in the described manner instead of my expected flow. I can make the code work the other way, for instance making it synchronous or removing throw statement and replacing it with Console.WriteLine("error");

OpenQA.Selenium.NoSuchElementException was unhandled by user code in c# selenium

I want to wait my selenium programe for max 30 seconds (GlobalVar.timetomaximumwait) with explicit wait.But when ever its unable to locate the element its pausing at wait.until(...) line and displaying OpenQA.Selenium.NoSuchElementException was unhandled by user code
If i press continue or press F10 its trying again to find the element and continuing the same for my defined time spam.
Not able to understand why the programme paused and the error message is coming in between.
I am using VS2010, c#, selenium 2.45,Ie 9
Any kind of help is much appreciated .
public string SetValueInTextBox(string InputData, string xPathVal)
{
try
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(GlobalVar.timetomaximumwait));
wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.XPath(xPathVal));
});
IWebElement TargetElement = driver.FindElement(By.XPath(xPathVal));
// IWebElement TargetElement = driver.FindElement(By.XPath(xPathVal));
elementHighlight(TargetElement);
TargetElement.Clear();
TargetElement.SendKeys(InputData);
//driver.FindElement(By.XPath(xPathVal)).SendKeys(InputData);
return "Pass";
}
catch (Exception e)
{
return "Fail";
}
finally
{
// string SSName = "temp.jpg";
TakeScreenshot("SetValueInTextBox");
}
}
The problem lies here:
wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.XPath(xPathVal));
});
You need to handle the exception that gets thrown when the element is not found.
wait.Until<IWebElement>((d) =>
{
try
{
return d.FindElement(By.XPath(xPathVal));
}
catch(NoSuchElementException e)
{
return null;
}
});
I would suggest adding in some logging into the catch block, so you know every time the driver fails to find the element.

How to do proper exception handling and return codes for Console Application

I have a simple C# console application that talks to a webservice. I am not sure if the way I am handling expections and returning error codes are proper. Would like to have comments on good pratice to do this. (I am using CommandLine.dll for option parsing)
class Program
{
static int Main(string[] args) {
var options = new Options();
if (CommandLine.Parser.Default.ParseArguments(args, options)) {
try {
var client = new MyWebService();
var response = client.MyFunction(new MyRequest() { Param1 = options.param1, Param2 = options.Param2 });
if (response.ErrorCode != 0) {
Console.WriteLine("Error code= " + response.ErrorCode);
}
else {
File.WriteAllText(options.OutputFile, response.File);
return 0;
}
}
catch (Exception exp) {
Console.WriteLine(exp.Message);
return 1;
}
}
return 1;
}
}
You should use the Console.Error.* to write errors. And I'll say that this:
if (response.ErrorCode != 0) {
Console.Error.WriteLine("Error code= " + response.ErrorCode);
return 1;
} else {
File.WriteAllText(options.OutputFile, response.File);
}
plus a final
return 0;
after the catch would be better, so that if you have multi-stage operations to do, it's easier to code (fall-through == ok, error == fast abort)
with multi-stage I mean:
call ws1
check for non-Exception errors of ws1, if errors abort
call ws2
check for non-Exception errors of ws2, if errors abort
call ws3
check for non-Exception errors of ws3, if errors abort
return success
In the cmd prompt, to save errors:
myprogram 2>err.txt
to redirect output + error:
myprogram > err.txt 2>&1
(the last one was taken from https://stackoverflow.com/a/1420981/613130)
Last thing: if the parsing of the arguments goes wrong, you should output an error.
You should refactor the MyWebService class and the MyFunction to throw exceptions if an error occurs. Instead of returning error codes. That gives you full flexibility in terms of who at which level can handle the errors and it makes the code much more readable.

How to rerun the try block if we encountered an error? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
C# cleanest way to write retry logic?
i having a function contains web service call to the server which fails sometime (unable to connect remote server error) due to some disturbance in network. The code is in try catch block. i want to rerun the web service call within try block so that the web call will be done successfully.
const int MaxRetries = 5;
for(int i = 0; i < MaxRetries; i++)
{
try
{
// do stuff
break; // jump out of for loop if everything succeeded
}
catch(Exception)
{
Thread.Sleep(100); // optional delay here
}
}
bool success = false;
int retry = 0;
while (!success && retry<3)
{
try{
// web service calls
success = true;
} catch(Exception) {
retry ++;
}
}
public void Connect()
{
Connect(1);
}
private void Connect(int num)
{
if (num > 3)
throw new Exception("Maximum number of attempts reached");
try
{
// do stuff
}
catch
{
Connect(num++);
}
}
You can put a loop around the try catch block like this:
bool repeat = true
while( repeat){
repeat = false;
try
{
...
}
catch( Exception )
{
repeat = true;
}
}
I think you have your answer here. I just wanted to add a couple of suggestions based on my abundant experience with this problem.
If you add logging to the catch block, you can ascertain how often the web service call fails, and how many attempts were made in all. (Maybe put a toggle in web.config to turn this logging off once the issue subsides.)
That information may prove useful in discussions with system administrators if, for example, the web service provider is within your organization, such as on an intranet.
In addition, if you find that the calls are still failing too often, you could introduce a delay in the catch, so that the retry is not immediate. You might only want to do that on the final attempt. Sometimes it is worth the wait for the user, who doesn't want to lose all the data they have just entered.
And finally, depending on the situation, you could add a Retry button to the UI, so that the user could keep trying. The user could choose to wait five minutes for the network problem to clear itself up, and click Retry.
Wrap the try/catch in a while loop. Set a flag on success to exit the while (or just break out). Make sure you have some sort of retry limit so it won't keep going forever.
while (true)
{
try
{
// call webservice
// handle results
break;
}
catch (TemporaryException e)
{
// do any logging you wish
continue;
}
catch (FatalException e)
{
// do any logging you wish
break;
}
}
If you want to limit the retries, change the termination condition on the while loop.
void Method()
{
do
{
try
{
DoStuff();
return;
}
catch (Exception e)
{
// Do Something about exception.
}
}
while (true);
}
If you find yourself wanting to do this frequently in your code, you might consider implementing a reusable class that encapsulates the "re-try when an error is encountered" logic. This way, you can ensure that the behavior is standardized throughout your code base, instead of repeated each time.
There's an excellent example available on Dan Gartner's blog:
public class Retrier<TResult>
{
public TResult Try(Func<TResult> func, int maxRetries)
{
return TryWithDelay(func, maxRetries, 0);
}
public TResult TryWithDelay(Func<TResult> func, int maxRetries, int delayInMilliseconds)
{
TResult returnValue = default(TResult);
int numTries = 0;
bool succeeded = false;
while (numTries < maxRetries)
{
try
{
returnValue = func();
succeeded = true;
}
catch (Exception)
{
//todo: figure out what to do here
}
finally
{
numTries++;
}
if (succeeded)
return returnValue;
System.Threading.Thread.Sleep(delayInMilliseconds);
}
return default(TResult);
}
}
Well, the easiest would be to copy the code to the catch-block, right?
Another approach could look like:
private void YourMethodThatTriesToCallWebService()
{
//Don't catch errors
}
public void TryToCallWebService(int numTries)
{
bool failed = true;
for(int i = 0; i < numTries && failed; i++)
{
try{
YourMethodThatTriesToCallWebService();
failed = false;
}catch{
//do nothing
}
}
}
You should put the entire catch block into a while statement:
while(retryCount < MAX_RETRY && !success)
{
try
{
//do stuff , calling web service
success = true;
}
catch
{
retryCount++
success = false;
}
}

Categories