Tuesday, September 17, 2013

Get Table Data

One of the most common tasks in our project is to retrieve data from a table in order to assert. With this post I will try to describe a unified way to get the required data from any table with a specific format so my assertions are well defined.
The assertion points to be well defined I usually prefer to have my actual data in the form of a Map(key,value) so my assertions are in the form
Assert.assertEquals(data.get(key),expected_value)
I selected the key value for my map to be the value of the first column with value a second map containing as keys the names of the columns and values the values of the columns.
Map(column_n_value:Map(column_2_name:column_2_value,…,column_n_name:column_n_value))
The aforementioned implementation for the key value pairs where chosen to be the table values instead of the table indexes for maintainability purposes. Maintainability wise a column addition or an non shorted table the index will return the wrong result while the value not.
The resulting assertions look like:
Assert.assertEquals(data.get(row_1_value).get(column_2_name),expected_value)
Assert.assertEquals(data.get(row_1_value).get(column_3_name),expected_value)
The first thing we need to do in order to construct our map is to get the number of rows and columns of the table as follows:
rows = selenium.getCssCount("css=table tbody tr").intValue()
columns = selenium.getCssCount("css=table tbody tr td").intValue()
With the number of rows and columns at hand the next step is to retrieve the names of the columns from the table header as follows in groovy:
public List<String> getTableColumnNames(){  
   def headerNames=[]  
   (1..selenium.getCssCount("css=table thead tr th").intValue()).each{columns->  
    if(!selenium.getText("css=table thead tr th" + ":nth-child(" + columns + ")").isEmpty()){  
      headerNames << selenium.getText("css=table thead tr th" + ":nth-child(" + columns + ")")  
    }
   return headerNames
}
Having the column names the next step is to construct the desired map as follows in groovy:
public HashMap<String, HashMap<String, String>> getTableInfo() {  
   selenium.waitForElement(componentName);  
   def TableMap=[:]    
   def columnNames = getTableColumnNames()  
   (1..selenium.getCssCount("css=table tbody tr").intValue()).each{row->  
     def columnMap=[:]
     (2..selenium.getCssCount("css=table tbody tr td").intValue()).each{column->  
      columnMap.put(columnNames[column-1],controller().getText(componentName + ":nth-child("+row+") *:nth-child("+column+")"))  
    }  
    TableMap.put(controller().getText(componentName + ":nth-child(" + row + ") td:nth-child(1)"),columnMap)  
   }  
   return TableMap;  
 }
The above implementation can be found embedded in Stevia, enriched with code detecting your locator style (Xpath, Css or Id).

The above implementation of the table scan could be altered to accept only td as columns by altering the column map to get
td:nth-child("+column+")
instead of
*nth-child("+column+")
Stevia includes similar methods such as: 
  • getTableInfoAsList 
  • getTableElements2DArray
  • getTableElementTextUnderHeader
Read More

Thursday, August 29, 2013

Regression Suites

In our agile project we utilized regression testing with test automation and continuous executions of our automated test scripts. This process served us well while the test execution lasted less than 4 hours but started to create major problems when the total number of test scripts increased and the execution time exceeded 1 day. A quick relief to our problem came with introduction of parallel execution of test scripts but still in cases were the management wanted a quick answer to “Do we deploy on production or not?” it wasn’t enough.

One way to design effective regression suites, to ensure the continuation of business functions, is to follow the rule:
Regression test suite!= sum(Functional test cases)
The purpose of the functional tests is to explore the behavior of specific business functions and highlight corner cases while the purpose of regression tests is to give an overall overview of the entire system. In this way the design of the regression suite should include test scripts inspecting the end-to-end business functions that a system encapsulates.

For the purpose of our project (agile environment) the regression suites were designed with the hypothesis that the GUI, boundary and GUI negative tests should be separated from the runtime tests because:
  1.      Changes in the code in boundaries are rare
  2.      GUI is indirectly tested (application its self is used for the preconditions of each test case)
This separation leads us automatically in designing different regression suites containing these special categories.

More over, a vertical separation according to execution frequency is needed. In an agile environment in a 2 weeks sprint, 4 sprints release circle, effective execution regression times could be at the end of each sprint, in every other sprint and at the end of the release. The schedule of each regression suite could be decided according to how often this part of the code is changed. For example a boundary test is highly unusual to change thus a planned execution time could be only once per release and preferably one sprint before the release sprint (latest spring usually reserved for sprint new features).



As mentioned before at the end of each sprint the new features suite should executed additively. For example in sprint 1 we have 5 features and in sprint 2 we have 3 features. The regression suite should have cases testing the 5 features (end of sprint 1) and cases 8 features (end of sprint 2)
Read More