• Blog
  • Info Support
  • Career
  • Training
  • International Group
  • Info Support
  • Blog
  • Career
  • Training
  • International Group
  • Search
logo InfoSupport
  • Latest blogs
  • Popular blogs
  • Experts
      • All
      • Bloggers
      • Speakers
  • Meet us
  • About us
    • nl
    • en
    • .NET
    • 3D printing
    • Advanced Analytics
    • Agile
    • Akka
    • Alexa
    • Algorithms
    • Api's
    • Architectuur
    • Artificial Intelligence
    • ATDD
    • Augmented Reality
    • AWS
    • Azure
    • Big Data
    • Blockchain
    • Business Intelligence
    • Chatbots
    • Cloud
    • Code Combat
    • Cognitive Services
    • Communicatie
    • Containers
    • Continuous Delivery
    • CQRS
    • Cyber Security
    • Dapr
    • Data
    • Data & Analystics
    • Data Science
    • Data Warehousing
    • Databricks
    • DataOps
    • Developers life
    • DevOps
    • Digital Days
    • Digital Twin
    • Docker
    • eHealth
    • Enterprise Architecture
    • Event Sourcing
    • Hacking
    • Infrastructure & Hosting
    • Innovatie
    • Integration
    • Internet of Things
    • Java
    • Machine Learning
    • Microservices
    • Microsoft
    • Microsoft Bot Framework
    • Microsoft Data Platform
    • Mobile Development
    • Mutation Testing
    • Open source
    • Pepper
    • Power BI
    • Privacy & Ethiek
    • Python
    • Quality Assistance & Test
    • Quality Assurance & Test
    • Requirements Management
    • Scala
    • Scratch
    • Security
    • SharePoint
    • Software Architecture
    • Software development
    • Software Factory
    • SQL Server
    • SSL
    • Start-up
    • Startup thinking
    • Stryker
    • Test Quality
    • Testing
    • TLS
    • TypeScript
    • Various
    • Web Development
    • Web-scale IT
    • Xamarin
    • All
    • Bloggers
    • Speakers
Home » Improving Performance codedUI playback on HtmlTable control
  • Improving Performance codedUI playback on HtmlTable control

    • By Marcel de Vries
    • .NET 9 years ago
    • .NET 0 comments
    • .NET .NET
    Improving Performance codedUI playback on HtmlTable control

    *Moved to: http://fluentbytes.com/improving-performance-codedui-playback-on-htmltable-control/

    Currently I am working for a customer on test automation with Microsoft CodedUI test to test a business web application. I am also working on a course on CodedUI testing. Based on that work I thought I would share some insights in playback performance on HTML tables in your application.

    A problem that we encountered the past few weeks is the extreme long wait times when we want to read data from a cell in a HTM Table. Today I took some time to see what was wrong with our code, and I can imagine the things I found will be beneficial to all of you running these types of tests.

    So the main thing I would like to point out here is the performance implications of not completely understanding how CodedUI gets the actual control from the browser and the fact that using a property of e.g. the HtmlTable control can significantly impact your playback performance. this is due to the fact that the property can constantly evaluate it’s value each time you call the property getter.

    To show the differences, I created a simple test page, where I have a table with 10 columns and 100 rows and each cell in the table has a different value. Now my UI test wants to pick out a cell and capture the value of that cell and assert that value. ( the red arrow pointing to an arbitrary cell I want to read the value from.

    image

    So the basic approach to getting this value would be, get the correct column index of the column we are interested in, next lookup the correct row that we are interested in and finally retrieve the value from the cell that we found.

    I evaluated three approaches, starting with the code I found in one of our actual projects that had an performance problem.

    So we can do this in 3 different ways all having different performance characteristics.

    The naive approach

    So lets take the first approach found in our codebase. Here  you create a loop where we loop through the header columns and match the cell.FriendlyValue of that column to the name of the column I am interested in and then keep that index when found. Next we do the same but now for the rows and we do the lookup in the cell with index 0. When we found the row we capture the value. Now in this approach we use the call to Htmltable.Getcel(rowIndex, ColumnIndex), that will return the cell and then we can get the value from the cell. The code is here below.

    int nIndex = 0;
    for (nIndex = 0; nIndex < table.ColumnCount; nIndex++)
    {
        var headerCell = table.GetCell(0, nIndex);
        if (headerCell.FriendlyName.Contains("Lookupcolumn9"))
        {
            break; // nIndex is the column index we are looking for
        }
    }
    string lookupValue="";
    int nRowIndex = 0;
    for (nRowIndex = 0; nRowIndex < table.Rows.Count; nRowIndex++)
    {
        var controlCell = table.GetCell(nRowIndex, 0);
        if (controlCell.FriendlyName.Contains("rowLookup85"))
        {
            var cell = table.GetCell(nRowIndex, nIndex);
            lookupValue = cell.FriendlyName;
            break;
        }
    }
    

    If you run this code, you will notice it is pretty slow in playback and there is a statement in this code that is incurring performance impact while you probably will never notice just based on looking at the code. If you look at the red highlighted line, you see a call in the loop to table.Rows.Count. When you call the getter of this property this will invoke a call to the browser DOM. this means we do this&nbsp; each time we iterate through the loop. the reason for the re-evaluation is that some javascript might have modified the table in the meanwhile and therefor it is re-evaluated each and every time we call the getter. So one simple performance improvement would be to cache the property local and use that as the test condition in the loop. The next thing that is incedibly slow, is the call to GetCell.

    Running the above code against the test page costs 160443 milliseconds to complete, so that is about 2.6 minutes to find that singe cell we are looking for.

    Using cached control values

    Now the second approach would be to use a foreach loop on the columns and the cells in stead of using an index into the rows and the columns. Now why is that a better approach you might ask yourself? Well when you use forach, you are using an itterator over a collection. the basic principal of an iterator is that the collection it iterates over is fixed during the iteration. so that means the values are all cached. to show you the code, have a look here below:

    string lookupValue = "";
    // find the index of the column we want to lookup in a row
    int columnIndex = -1;
    foreach (HtmlCell header in ((HtmlRow)(table.Rows[0])).Cells)
    {
        if (header.FriendlyName.Contains("Lookupcolumn9"))
        {
            columnIndex = header.ColumnIndex;
            break;
        }
    }
    
    foreach (HtmlRow row in table.Rows)
    {
    
        if (row.Cells[0].FriendlyName.Contains("rowLookup85"))
        {
            // get the value and return
            lookupValue = row.Cells[columnIndex].FriendlyName;
            break;
        }
    }
    
    

    When I execute this test, this results in resolving the right cell in about 28 seconds. So just using a foreach in stead of the for loop saves a lot of time. Now the actual culprit of the problem is more or less that we just can’t tell which properties are cached value and which are not. But just using the iterator will ensure you iterate over a fixed collection.

    Leveraging CodedUI control search

    This algorithm still has a big problem in my opinion. The problem is that if I want to get the first column out of the first row, this call function would return pretty fast, since all loops only execute once. But when I want the last column from the last row, my performance degrades tremendous!

    So the third approach you can take is just by leveraging the search capabilities of CodedUI yourself. What i mean by that is that we are going to create a HtmlCell control and control it’s search properties to find the right header column right away and from that we determine the index that this cell has in the row, Next we create an HtmlRow control, and use it’s search properties to find the row that contains the value we are looking for, identifying the right row. The last step s to take the FriendlyName of the cell with the index we found in the previous search. The code below shows how this is done:

    HtmlCell directcell = new HtmlCell(browser);
    directcell.SearchProperties.Add(new PropertyExpression(HtmlCell.PropertyNames.InnerText,
    "Lookupcolumn9", PropertyExpressionOperator.Contains)); int cellIndex = directcell.ColumnIndex; HtmlRow directRow = new HtmlRow(browser); directRow.SearchProperties.Add(new PropertyExpression(HtmlRow.PropertyNames.InnerText,
    "rowLookup85", PropertyExpressionOperator.Contains)); var lookupValue = directRow.Cells[cellIndex].FriendlyName;

    The big advantage of this approach is that we leverage the capabilities of codedUI to find the stuff we are looking for and not employing our own algorithm to find stuff in the table. Next advantage is that this algorithm will have the same performance regardless of the cell we are looking for and finally the performance of this approach is incredible faster then the previous approaches. On my machine this code takes about 895 milliseconds to complete.

    Summary

    So to summarize you can see how you can speed up CodedUI by looking at the algorithms you invent and if you stay as close as possible to the capabilities of coded UI you can achieve a tremendous speedup. in my example the speedup is from 2.6 Minutes, to 28 seconds to 895 milliseconds. This is a speedup of nearly 180 times!

    One thing to note is that this approach only works when the values we are looking for in the header and first column are unique in the table. Otherwise the search would result in multiple controls being found and the the Filter Properties will be used to find the actual control. if it cannot discriminate it will just return the first control in the list, but that does not necessary be the cell you are looking for.

    As I mentioned in the introduction, Currently I am working on a training that has many more details on how to improve the playback performance of your CodedUi tests in different ways. This course will be published online at pluralsight.com hopefully early summer 2013. I will update the reference to that course as soon as it is available.

    Follow my new blog on http://fluentbytes.com

    Share this

Marcel de Vries

View profile

Related IT training

Go to training website

Related Consultancy solutions

Go to infosupport.com

Related blogs

  • Building a custom Kubernetes operator in C#

    Building a custom Kubernetes operator in C# Willem Meints - 1 month ago

  • Transform SpecFlow Table Column

    Transform SpecFlow Table Column Ronald Bosma - 3 months ago

  • Building a passwordless login flow with WebAuthn and th…

    Building a passwordless login flow with WebAuthn and th… Willem Meints - 5 months ago

Data Discovery Channel

  • Modern Data Platform

  • Gartner Data & Analytics Summit 2022

  • De Data Architecture ®Evolution

Nieuwsbrief

* verplichte velden

Contact

  • Head office NL
  • Kruisboog 42
  • 3905 TG Veenendaal
  • T +31 318 552020
  • Call
  • Mail
  • Directions
  • Head office BE
  • Generaal De Wittelaan 17
  • bus 30 2800 Mechelen
  • T +32 15 286370
  • Call
  • Mail
  • Directions

Follow us

  • Twitter
  • Facebook
  • Linkedin
  • Youtube

Newsletter

Sign in

Extra

  • Media Library
  • Disclaimer
  • Algemene voorwaarden
  • ISHBS Webmail
  • Extranet
Beheer cookie toestemming
Deze website maakt gebruik van Functionele en Analytische cookies voor website optimalisatie en statistieken.
Functioneel Always active
De technische opslag of toegang is strikt noodzakelijk voor het legitieme doel het gebruik mogelijk te maken van een specifieke dienst waarom de abonnee of gebruiker uitdrukkelijk heeft gevraagd, of met als enig doel de uitvoering van de transmissie van een communicatie over een elektronisch communicatienetwerk.
Voorkeuren
De technische opslag of toegang is noodzakelijk voor het legitieme doel voorkeuren op te slaan die niet door de abonnee of gebruiker zijn aangevraagd.
Statistieken
De technische opslag of toegang die uitsluitend voor statistische doeleinden wordt gebruikt. De technische opslag of toegang die uitsluitend wordt gebruikt voor anonieme statistische doeleinden. Zonder dagvaarding, vrijwillige naleving door uw Internet Service Provider, of aanvullende gegevens van een derde partij, kan informatie die alleen voor dit doel wordt opgeslagen of opgehaald gewoonlijk niet worden gebruikt om je te identificeren.
Marketing
De technische opslag of toegang is nodig om gebruikersprofielen op te stellen voor het verzenden van reclame, of om de gebruiker op een website of over verschillende websites te volgen voor soortgelijke marketingdoeleinden.
Manage options Manage services Manage vendors Read more about these purposes
Voorkeuren
{title} {title} {title}