4

I am looking for some suggestions on how to deal with a slight timing issue that I am having with two controls on a page. I have a list box that contains up to 10 items, however the contents of the box can change depending on what characters are entered into the search box.

For example: I enter zi into the search box and the list box will begin to populate Zimmerman...

The trouble that I am running into is that there is enough of a delay between entering the text and when I use WatiN to click the button to change the view to that of the top item in the list.

So here is what I currently have in my

            browser.Page<AVDashboardPage>().SearchBox.TypeText("zi");
            browser.Page<AVDashboardPage>().ChangeActBtn.Click();

What I am considering doing is to simply write a small method in the AVDashboard page class that contains a small delay.

    public void EnterSearchTextWithDelay(string searchText)
    {
        SearchBox.TypeText(searchText);
        System.Threading.Thread.Sleep(200);

    }

I suppose I could also do it by adding a while loop that would look in the listbox for a corresponding element but I worry that could be pretty slow.


So my method ended up looking like this:

public void EnterSearchTextWithDelay(string searchText)
{
    SearchBox.TypeText(searchText);
    int counter = 0;
    while(!AccountList.Text.Contains(searchText) && counter < 50)
    {
        System.Threading.Thread.Sleep(10);
        counter++;
    }

}

4 Answers 4

1

Sleeps should be used sparingling in an automated test because they halt execution for the given period of time. A much better solution to help control race conditions or sync problems in a test is to use a polling loop similar to:

int pollCount = 0;
while (listbox_element_not_found && pollCount < maxPollCount) 
{
  System.Threading.Thread.Sleep(10);
  pollCount++;
}

Of course if you timeout of the loop you probably want to branch with an if statement and mark the test as inconclusive to investigate.

I would also suggest that instead of looking for an element in a listbox (esp. since the list items change) that if the ChangeActBtn is 'grayed' while the listbox is being populated you craft a polling loop that detects when the button control changes its enabled property from false to true (if the dev's gray the button while the list is being populated).

IMHO, this latter approach makes the test more robust because you are not tied to matching a specific element in a listbox, and also improves design because essentially it is clicking the button "too soon" that is causing the sync issue between your test and the AUT so this hinges test execution on the button control and not on the listbox.

1
  • Huh - maybe I took a class from you or something. My code ended up looking a great deal like that.
    – Dan Snell
    Jun 9, 2011 at 17:12
2

Rather than using sleep, you should check for the element in the listbox, you might think this is the slow approach, but you will have a more reliable test, which will save you time in the long run.

Just make sure you remember to have a timeout in the loop in case the item for some reason never exists.

1
  • Yeah that is probably what I will end up doing. Using sleeps is bad form I realize. :)
    – Dan Snell
    Jun 8, 2011 at 22:54
0

If the list is being pulled dynamically via AJAX as text is being typed, you can implement a WaitForAsyncPostbackToComplete to wait for the list to completely populate before selecting and clicking the button.

You can look at the following blog post for the code to implement the WaitForAsyncPostbackToComplete method. This will reduce your wait time to the bare minimum and ensure stability of the code.

0

The answer really depends on the implementation of the dynamic load

If you are using jQuery then you can implement something like this solution from https://stackoverflow.com/questions/2375709/how-to-wait-for-jquery-ajax-requests-to-complete-from-watin.

public static class BrowserExtensions
{
    public static void WaitForAjaxRequest( this Browser browser )
    {
        int timeWaitedInMilliseconds = 0;
        var maxWaitTimeInMilliseconds = Settings.WaitForCompleteTimeOut*1000;

        while ( browser.IsAjaxRequestInProgress()
                && timeWaitedInMilliseconds < maxWaitTimeInMilliseconds )
        {
            Thread.Sleep( Settings.SleepTime );
            timeWaitedInMilliseconds += Settings.SleepTime;
        }
    }

    public static bool IsAjaxRequestInProgress( this Browser browser )
    {
        var evalResult = browser.Eval( "watinAjaxMonitor.isRequestInProgress()" );
        return evalResult == "true";
    }

    public static void InjectAjaxMonitor( this Browser browser )
    {
        const string monitorScript =
            @"function AjaxMonitor(){"
            + "var ajaxRequestCount = 0;"

            + "$(document).ajaxStart(function(){"
            + "    ajaxRequestCount++;"
            + "});"

            + "$(document).ajaxComplete(function(){"
            + "    ajaxRequestCount--;"
            + "});"

            + "this.isRequestInProgress = function(){"
            + "    return (ajaxRequestCount > 0);"
            + "};"
            + "}"

            + "var watinAjaxMonitor = new AjaxMonitor();";

        browser.Eval( monitorScript );
    }
}

This solution is for the ajax control toolkit, which I s based on this blog post.

            bool isInPostback = true;
            while (isInPostback)
            {

                // the exception is to handle if the page does not have the ajax control toolkit code ready and loaded
                try
                {
                    // This line of code calls javascript in browser to execute the ajax toolkit's
                    // get_isInAsyncPostBack method
                    isInPostback = Convert.ToBoolean(_ie.Eval("Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack();"));

                }
                catch (WatiN.Core.Exceptions.JavaScriptException e)
                {
                    if (e.Message == "TypeError: 'Sys' is undefined")
                    {
                        isInPostback = false;
                        break;
                    }
                    else
                    {
                        throw;
                    }
                }

                // This exception is being thrown when running under impersonation
                // I think the eval needs an impersonation call first
                catch (WatiN.Core.Exceptions.RunScriptException scriptException)
                {
                    Debug.Print(scriptException.Message);
                    isInPostback = false;
                }
                if (isInPostback)
                {
                    //sleep for 200ms and query again  
                    System.Threading.Thread.Sleep(200);
                }
            }
        }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.