views:

516

answers:

2

We're using Watin for acceptance tests and we find it gets mighty slow once we have Web pages that are more than 100K of HTML source.

I've a feeling that some of the speed issues come from iterating over HTML tables. Some of our tables have 50 - 60 rows, each with 5 - 10 columns, and this makes Watin quite slow when searching for items on the page.

Does anyone have specific recommendations on (for instance) the best overloads of the element search methods to use? Are there specific methods to avoid because they're really slow?

A: 

You can speed up a bit with adding IDs to html table rows or columns elements. So in your case where you have less columns it is probably easier to add ids to at least columns. (Specially because numbers of rows is probably changing).

So instead of

string price = ie.Table(Find.ById("name")).TableRows[i].TableCells[i].Text;

with this changes in html

<table id="name">
<tr id='total'>             
            <td id='price'>
                $1.00
            </td>
        </tr>
</table>

without iteration

string total = ie.TableRow(Find.ByID("total")).TableCell(Find.ById("price")).Text;

or only one iteration

ie.Table(Find.ById("name")).TableRows[i].TableCell(Find.ById("price")).Text;
andreja
I find that hard to believe. Does WatiN index the elements on ID and does it not setup arrays for elements?
Martin
+1  A: 

What I have done to help speed up processing of Table elements is written a extension method to Iterate over table rows by calling NextSibling on a table row instead of calling the .TableRows property which can be slow.

public static class IElementContainerExtensions
{
    /// <summary>
    /// Safely enumerates over the TableRow elements contained inside an elements container.
    /// </summary>
    /// <param name="container">The IElementsContainer to enumerate</param>
    /// <remarks>
    /// This is neccesary because calling an ElementsContainer TableRows property can be an
    /// expensive operation.  I'm assuming because it's going out and creating all of the
    /// table rows right when the property is accessed.  Using the itterator pattern below
    /// to prevent creating the whole table row hierarchy up front.
    /// </remarks>
    public static IEnumerable<TableRow> TableRowEnumerator( this IElementContainer container )
    {
        //Searches for the first table row child with out calling the TableRows property
        // This appears to be a lot faster plus it eliminates any tables that may appear
        // nested inside of the parent table

        var tr = container.TableRow( Find.ByIndex( 0 ) );
        while ( true )
        {
            if ( tr.Exists )
            {
                yield return tr;
            }
            //Moves to the next row with out searching any nested tables.
            tr = tr.NextSibling as TableRow;
            if ( tr == null || !tr.Exists )
            {
                break;
            }
        }
    }
}

All you need to do is get a reference to a Table and it will find the first tr and iterate over all of it's siblings.

foreach ( TableRow tr in ie.Table( "myTable" ).TableRowEnumerator() )
{
    //Do Someting with tr
}
Aaron Carlson