Retrieving Data From Access Reports

Applies to TestComplete 15.20, last modified on January 19, 2022

Microsoft Access includes a powerful report building engine. However, when you double-click a report in the database window to view it, Access displays the printable version of the report in the Print Preview window. This window displays the report as it will be printed and does not contain any child controls that provide access to report data. So, how do you retrieve the data from the report?

A possible solution is to use the optical character recognition. In this case, you will have to write code for scrolling operations. In addition, the recognition may give incorrect results for wrapped lines.

Another possible solution is to export the report data to a file and then analyze the exported data. This approach is easier to implement and it does not depend on the way the data is displayed (no “text wrapping” problems). Below you will see how you can export a report and obtain data from a file.

In further explanations, we will use the Summary of Sales by Year report from the Northwind database that is shipped with Microsoft Access.

Microsoft Access lets you export a report to files of various formats (snapshot, Microsoft Excel, HTML, RTF and others). We will export the report to an HTML file since it is easier to retrieve summary data from it.

To export the report to the HTML format, follow these steps (you can record a script to automate the exporting actions in the future):

  • Right-click on the report in the Database window and select Export from the context menu. Microsoft Access will display the Export Report dialog, where you can select the file name and format.
  • Specify the desired file name in the File name edit field.
  • Select HTML in the Save as type box.
  • Press Export.
  • Microsoft Access will display the HTML Output Options dialog, where you can specify additional export parameters.
  • Do not change fields in this dialog and press OK.

Microsoft Access will create a new HTML file holding the exported data. Now we can parse the file to obtain the report data and retrieve summary values from it.

To parse the file, we will use the TestComplete Web Testing plugin. This plugin provides access to elements of the web page that is shown in Internet Explorer, Firefox or a WebBrowser control.

To parse the exported data file:

  • Open the HTML file in your Internet browser.
  • Switch to the TestComplete Object Browser panel and explore the page contents.
  • As you can see, the page contains a lot of element names like Item(nn). If you use the DOM model, the object tree will look like this:

To determine the name of the desired web page elements take advantage of the Object Spy window and its target glyph:

  • Click the Display Object Spy button on the Tools toolbar. TestComplete will display the Object Spy window.
  • Drag the target glyph () from the Object Spy window to the web page. While dragging, a red rectangle will appear around windows, controls and web page elements that TestComplete recognizes as objects.
  • Release the mouse button over the Totals for 1996 text. The Object Spy window will display properties and methods of the selected web page element. The Name property will contain the string that lets you address the element in scripts.
  • Similarly you can obtain the scripting name for all summary cells. In our examples they are --

    Item(73)  - Totals for 1996

    Item(154) - Totals for 1997

    Item(213) - Totals for 1998

Now we can write a script that will retrieve data from the page:

JavaScript

function ParseHTML()
{

  // Obtains the page object
  Browsers.Item(btIExplorer).Run("file:///C:\\Summary of Sales by Year.html");
  let page = Sys.Browser("iexplore").Page("*");

  // Totals for 1996
  let cell = page.contentDocument.all.item(73);// Obtains the page element
                                    // 73 is the index which we obtain through the Object Spy window
  let row = GetRow(cell); // Obtains the table row to which the element belongs
  let s = ProcessData(row);// Retrieves values from the row's cells
  Log.Message(s); // Posts data to the log

  // Totals for 1997
  cell = page.contentDocument.all.item(151);
  row = GetRow(cell);
  s = ProcessData(row);
  Log.Message(s);

  // Totals for 1998
  cell = page.contentDocument.all.item(208);
  row = GetRow(cell);
  s = ProcessData(row);
  Log.Message(s);
}

function ProcessData(ARow)
{
  var cell, s;

  // Obtains the value of the first column
  cell = ARow.cells.item(1);
  s = cell.innerText;

  // Obtains the value of the second column
  cell = ARow.cells.item(2);
  s = s + " Orders shipped: " + cell.innerText;

  // Obtains the value of the third column
  cell = ARow.cells.item(3);
  s = s + " Sales: " + cell.innerText;

  return s;
}

function GetRow(AObj)
{
  if (!strictEqual(AObj, null))
  {
    if (equal(AObj.tagName, "TD"))
      return AObj.parentElement;
    else
      return GetRow(AObj.parentElement);
  }
  else
    return null;
}

JScript

function ParseHTML()
{

  // Obtains the page object
  Browsers.Item(btIExplorer).Run("file:///C:\\Summary of Sales by Year.html");
  var page = Sys.Browser("iexplore").Page("*");

  // Totals for 1996
  var cell = page.contentDocument.all.item(73);// Obtains the page element
                                    // 73 is the index which we obtain through the Object Spy window
  var row = GetRow(cell); // Obtains the table row to which the element belongs
  var s = ProcessData(row);// Retrieves values from the row's cells
  Log.Message(s); // Posts data to the log

  // Totals for 1997
  cell = page.contentDocument.all.item(151);
  row = GetRow(cell);
  s = ProcessData(row);
  Log.Message(s);

  // Totals for 1998
  cell = page.contentDocument.all.item(208);
  row = GetRow(cell);
  s = ProcessData(row);
  Log.Message(s);
}

function ProcessData(ARow)
{
  var cell, s;

  // Obtains the value of the first column
  cell = ARow.cells.item(1);
  s = cell.innerText;

  // Obtains the value of the second column
  cell = ARow.cells.item(2);
  s = s + " Orders shipped: " + cell.innerText;

  // Obtains the value of the third column
  cell = ARow.cells.item(3);
  s = s + " Sales: " + cell.innerText;

  return s;
}

function GetRow(AObj)
{
  if (AObj != null)
  {
    if (AObj.tagName == "TD")
      return AObj.parentElement;
    else
      return GetRow(AObj.parentElement);
  }
  else
    return null;
}

Python

def ParseHTML():

  # Obtains the page object
  Browsers.Item[btIExplorer].Run("file:///C:\\Summary of Sales by Year.html");
  page = Sys.Browser("iexplore").Page("*");

  # Totals for 1996
  cell = page.contentDocument.all.item(73); # Obtains the page element
                                            # 73 is the index which we obtain through the Object Spy window
  row = GetRow(cell); # Obtains the table row to which the element belongs
  s = ProcessData(row); # Retrieves values from the row's cells
  Log.Message(s); # Posts data to the log

  # Totals for 1997
  cell = page.contentDocument.all.item(151);
  row = GetRow(cell);
  s = ProcessData(row);
  Log.Message(s);

  # Totals for 1998
  cell = page.contentDocument.all.item(208);
  row = GetRow(cell);
  s = ProcessData(row);
  Log.Message(s);

def ProcessData(ARow):
  # Obtains the value of the first column 
  cell = ARow.cells.item(1);
  s = cell.innerText;

  # Obtains the value of the second column
  cell = ARow.cells.item(2);
  s = s + " Orders shipped: " + cell.innerText;

  # Obtains the value of the third column 
  cell = ARow.cells.item(3);
  s = s + " Sales: " + cell.innerText; 

  return s;

def GetRow(AObj):
  if (AObj != None):
    if (AObj.tagName == "TD"):
      return AObj.parentElement;
    else:
      return GetRow(AObj.parentElement);
  else:
    return None;

VBScript

Sub ParseHTML
  Dim page, cell, row, s

  ' Obtains the page object
  Browsers.Item(btIExplorer).Run("file:///C:\Summary of Sales by Year.html")
  Set page = Sys.Browser("iexplore").Page("*")

  ' Totals for 1996
  Set cell = page.contentDocument.all.item(73) ' Obtains the page element
                                        ' 73 is the index which we obtain through the Object Spy window
  Set row = GetRow(cell) ' Obtains the table row to which the element belongs
  s = ProcessData(row) ' Retrieves values from the row's cells
  Log.Message s ' Posts data to the log

  ' Totals for 1997
  Set cell = page.contentDocument.all.item(151)
  Set row = GetRow(cell)
  s = ProcessData(row)
  Log.Message s

  ' Totals for 1998
  Set cell = page.contentDocument.all.item(208)
  Set row = GetRow(cell)
  s = ProcessData(row)
  Log.Message s
End Sub

Function ProcessData(ARow)
  Dim cell, s

  ' Obtains the value of the first column
  Set cell = ARow.cells.item(1)
  s = cell.innerText
  
  ' Obtains the value of the second column
  Set cell = ARow.cells.item(2)
  s = s + " Orders shipped: " + cell.innerText
  
  ' Obtains the value of the third column
  Set cell = ARow.cells.item(3)
  s = s + " Sales: " + cell.innerText

  ProcessData = s
End Function

Function GetRow(AObj)
  If Not (AObj Is Nothing) Then
    If AObj.tagName = "TD" Then
      Set GetRow = AObj.parentElement
    Else
      Set GetRow = GetRow(AObj.parentElement)
    End If 
  Else
    Set GetRow = Nothing
  End If 
End Function

DelphiScript

// Forward declarations
function ProcessData(ARow); forward;
function GetRow(AObj); forward;

procedure ParseHTML;
var
  page, cell, row, s : OleVariant;
begin
  // Obtains the page object
  Browsers.Item(btIExplorer).Run('file:///C:\Summary of Sales by Year.html');
  page := Sys.Browser('iexplore').Page('*');

  // Totals for 1996
  cell := page.contentDocument.all.item(73);// Obtains the page element
                                     // 73 is the index which we obtain through the Object Spy window
  row := GetRow(cell);  // Obtains the table row to which the element belongs
  s := ProcessData(row);// Retrieves values from the row's cells
  Log.Message(s);       // Posts data to the log

  // Totals for 1997
  cell := page.contentDocument.all.item(151);
  row := GetRow(cell);
  s := ProcessData(row);
  Log.Message(s);

  // Totals for 1998
  cell := page.contentDocument.all.item(208);
  row := GetRow(cell);
  s := ProcessData(row);
  Log.Message(s);
end;

function ProcessData(ARow);
var
  cell, s : OleVariant;
begin
  // Obtains the value of the first column
  cell := ARow.cells.item(1);
  s := cell.innerText;

  // Obtains the value of the second column
  cell := ARow.cells.item(2);
  s := s + ' Orders shipped: ' + cell.innerText;

  // Obtains the value of the third column
  cell := ARow.cells.item(3);
  s := s + ' Sales: ' + cell.innerText;

  Result := s;
end;

function GetRow(AObj);
begin
  if AObj <> nil then
  begin
    if AObj.tagName = 'TD' then
      Result := AObj.parentElement
    else
      Result := GetRow(AObj.parentElement);
  end
  else
    Result := nil;
end;

C++Script, C#Script

function ParseHTML()
{

  // Obtains the page object
  Browsers["Item"](btIExplorer)["Run"]("file:///C:\\Summary of Sales by Year.html");
  var page = Sys["Browser"]("iexplore")["Page"]("*");

  // Totals for 1996
  var cell = page["contentDocument"]["all"]["item"](73);// Obtains the page element
                                             // 73 is the index which we obtain through the Object Spy window
  var row = GetRow(cell); // Obtains the table row to which the element belongs
  var s = ProcessData(row);// Retrieves values from the row's cells
  Log["Message"](s); // Posts data to the log

  // Totals for 1997
  cell = page["contentDocument"]["all"]["item"](151);
  row = GetRow(cell);
  s = ProcessData(row);
  Log["Message"](s);

  // Totals for 1998
  cell = page["contentDocument"]["all"]["item"](208);
  row = GetRow(cell);
  s = ProcessData(row);
  Log["Message"](s);
}

function ProcessData(ARow)
{
  var cell, s;

  // Obtains the value of the first column
  cell = ARow["cells"]["item"](1);
  s = cell["innerText"];

  // Obtains the value of the second column
  cell = ARow["cells"]["item"](2);
  s = s + " Orders shipped: " + cell["innerText"];

  // Obtains the value of the third column
  cell = ARow["cells"]["item"](3);
  s = s + " Sales: " + cell["innerText"];

  return s;
}

function GetRow(AObj)
{
  if (AObj != null)
  {
    if (AObj["tagName"] == "TD")
      return AObj["parentElement"];
    else
      return GetRow(AObj["parentElement"]);
  }
  else
    return null;
}

The main routine is ParseHTML. At the beginning, this routine obtains the Page object that corresponds to the tested web page. In the given example, the page belongs to the explorer process, not to iexplore (see About Web Object Identification and Object Models for more information). Then, the routine obtains the web page element by its index, retrieves data and posts them to the log:

JavaScript, JScript

cell = Page.document.all.item(73);
row = GetRow(cell);
s = ProcessData(row);
Log.Message(s);
// ...

Python

cell = Page.document.all.item(73);
row = GetRow(cell);
s = ProcessData(row);
Log.Message(s);
# ...

VBScript

Set cell = Page.document.all.item(73)
Set row = GetRow(cell)
s = ProcessData(row)
Log.Message s
' ...

DelphiScript

cell := Page.document.all.item(73);
row := GetRow(cell);
s := ProcessData(row);
Log.Message(s);
// ...

C++Script, C#Script

cell = Page["document"]["all"]["item"](73);
row = GetRow(cell);
s = ProcessData(row);
Log["Message"](s);
// ...

The cell variable refers to the web page element that contains the summary text. We pass this variable to the GetRow function that returns the TR element for the desired cell. Why do we use a special routine and do not obtain the TR by using the cell.parentElement property? The text may belong to a child element (B, I, FONT) rather than TD. In our example, the text belongs to the FONT element. The whole hierarchy looks like --

<TR>

<TD>

<B>

<I>

<FONT>text</FONT>

...

The special GetRow function that we wrote walks up the hierarchy and returns the TR element containing summary values.

After we obtain the TR element, we pass it to the ProcessData function for processing. This function uses the call.items method to obtain the TD elements and then uses their innerText property to get values:

JavaScript, JScript

// Obtains the value of the first column
cell = ARow.cells.item(1);
s = cell.innerText;
// ...

Python

# Obtains the value of the first column
cell = ARow.cells.item(1);
s = cell.innerText;
# ...

VBScript

' Obtains the value of the first column
Set cell = ARow.cells.item(1)
s = cell.innerText
' ...

DelphiScript

// Obtains the value of the first column
cell := ARow.cells.item(1);
s := cell.innerText;
// ...

C++Script, C#Script

// Obtains the value of the first column
cell = ARow["cells"]["item"](1);
s = cell["innerText"];
// ...

After you execute the script, you will see the report data in the test log.

See Also

Testing Microsoft Access Applications
Classic Web Testing
Object Spy
Optical Character Recognition

Highlight search results