Featured

QTP 10 not Recognizing Oracle Forms Obje

Bidun Meduza shared on our Facebook page a problem and solution that I thought might be useful to you too. Problem He had a situation where QTP 10 was unable to recognize objects of Oracle Forms. Th

Read More
QTP 10 not Recognizing Oracle Forms Objects

New Navigation Menus

In order to ease orientation through our vast knowledge archive, we have added the following navigation menus: First Steps Scripting QTP VBScript Tutorial Knowledge Articles (by levels of expertise) B

Read More
New Navigation Menus

Permalinks

We’ve changed the permalinks format, so if you have been experiencing some trouble with finding materials with the old links, then remove the YYYY/MM/ from the URL and you’d be fine. In ca

Read More
Permalinks

Advanced QTP on Android

A first version of AdvancedQTP for Android can be found here. Now you can take this perfect companion with you to keep updated on what’s new in our site.

Read More
Advanced QTP on Android

New Advanced QTP Features: Layout and FB

As you have most surely noticed, we’ve recently changed the site’s theme in order to enhance the overall user experience. As from today, you can also register and login seamlessly with you

Read More
New Advanced QTP Features: Layout and FB Integration

Universal class for data manipulations (Russian)

0
by on April 11, 2009 at 07:18

Sergey Talalaev (SQAdotBY) has contributed this wonderful article on using a generic class for all your Excel data-operations needs. Thanks SQAdotBY, for you hard work, and willingness to share it with the QTP community! This article is copyrighted to Sergey.

An English translation is available here.

Универсальный класс для работы с данными.

Содержание:

 

1. Введение.

2. Excel data manipulation.

2.1. Основные задачи.

2.2. Предварительные шаги.

2.3. Со стороны функциональной библиотеки.

2.4. Со стороны тестового скрипта.

3. Выводы.

 

1. Введение

Влияние мирового кризиса к сожалению сказалось и на моей персоне и мне потребовалось срочно и по возможности глубоко изучить новый для себя фреймворк – Quick Test Professional.

У меня уже был опыт работы с продуктами HP Mercury, но он относился к предыдущей линейке, я имею в виду WinRunner. Естественно я ожидал некоторого сходства в процессах скриптостроения и организации самого фреймворка. Поэтому по свежим следам постараюсь изложить замеченные мною интересные моменты и привести свои примеры реализации некоторых функций.

2. Работа с Excel-данными

Еще работая с WinRunner, я убедился, что встроенная реализация Excel хранилища не настолько гибка, как мне бы хотелось. Поэтому, как всегда, я приготовился к миграции своих процедур для работы с Excel. И был немного удивлен отсутствию встроенных средств работы с БД. Но это задача вполне по силам, когда за плечами вся мощь VB.

2.1. Основные задачи

Итак, для начала определимся, зачем нам это нужно.

Я всегда выступал за то, чтобы подготовка тестовых данные для автоматических тестов была максимально упрощена с одной стороны, и, по возможности, исключала возможность ошибок при вводе с другой.

Эти трудносовместимые вещи отлично реализуются посредством встроенной в Excel валидации входных данных и также прекрасно уничтожаются попыткой редактирования Excel таблицы напрямую из QTP.

Более того уничтожается также любое кастомное форматирование, без которого нормально читаемая таблица превращается в клетчатый текст.

Было до вмешательства из QTP:

clip_image002

Стало после редактирования из QTP:

clip_image004

Чтобы избежать подобных казусов я давно применяю прямую вычитку из Excel файлов в массивы, используя для этого стандартные ODBC источники. Данная техника успешно прижилась уже на следующих тестовых фреймворках: Rational Robot, IBM Rational Functional Tester, WinRunner и надеюсь QTP

Помимо решения вышеперечисленных проблем мы избегаем также серьезного, на мой взгляд, ограничения по использованию одной таблицы для одного Excel sheet.

2.2. Предварительные шаги

В Excel документах имеется функциональность которая позволяет выделять значимые для пользователя подмножества ячееек в специальные структуры. Такие структуры называемые “именованными диапазонами” обеспечивают возможность обращаться к ним к ним через логические имена.

Кроме того (что гораздо более значимо для нас) такие диапазоны становятся видны как стандартные ODBC таблицы из внешних приложений.

Итак, для оформления требуемой совокупности ячеек в качестве “именованного диапазона” необходимо выполнить следющую последовательность действий:

- выделить все ячейки, планируемые для оформления в именованный диапазон

- создать именованный диапазон через “Define name” диалог (Formulas > Define Name)

clip_image006

или напрямую введя имя диапазона в Navigation Bar.

clip_image008

Для проверки корректности вновь созданного именованного диапазона – выделите все ячейки диапазона и проверьте значение в Navigation Bar. Он должен содержать логическое имя вместо A1 нотации.

Важно отметить одно требование обязательно при использовании именованных диапазонов в качестве источника данных:

- первая строка нашего диапазона должна содержать имена столбцов, а не данные

2.3. Со стороны функциональной библиотеки

После всех подготовительных шагов осталось совсем немного поработать руками, а точнее пописать код.

Раз уж QTP 9.5 предоставил нам замечательную возможность работать c “почти” объектами – грех было бы ей не воспользоваться. Поэтому весь наш функционал мы гордо завернем в класс с благозвучным названием TestData.

Стоит напомнить, что QTP не видит напрямую классы, объявленные во внешних библиотеках, поэтому для каждого класса должна присутствовать функция создания экземпляра класса, в данном случае – CreateTestData

Кроме того мы должны иметь возможность инициировать наш класс не только через загрузку из Excel источника но и напрямую из кода. Именно для этих целей появились два метода: SetData и GetData

 

 

<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> CreateTestData ()
    <span style="color: #0000ff;">Set</span> CreateTestData = <span style="color: #0000ff;">new</span> TestData
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

<span style="color: #0000ff;">Class</span> TestData

    <span style="color: #0000ff;">Private</span> mTestTable()

    <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Initialize()
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Terminate()
        <span style="color: #0000ff;">Erase</span> mTestTable
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #008000;">\' @Documentation Initiates TestData object with external data</span>
    <span style="color: #008000;">\'-----------------------------------------------------------</span>
    <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> SetData (DataArr())
        <span style="color: #0000ff;">Dim</span> i,j
        <span style="color: #0000ff;">ReDim</span> mTestTable(UBound(DataArr, 1), UBound(DataArr, 2))

        <span style="color: #0000ff;">For</span> i=0 <span style="color: #0000ff;">to</span> UBound(DataArr, 1)
            <span style="color: #0000ff;">For</span> j=0 <span style="color: #0000ff;">to</span> UBound(DataArr, 2)
                mTestTable(i,j) = DataArr(i,j)
            <span style="color: #0000ff;">Next</span>
        <span style="color: #0000ff;">Next</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #008000;">\' @Documentation Extracts data from TestData object</span>
    <span style="color: #008000;">\'---------------------------------------------------</span>
    <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> GetData(DataArr())
        <span style="color: #0000ff;">Dim</span> i,j

        <span style="color: #0000ff;">ReDim</span> DataArr(UBound(mTestTable, 1), UBound(mTestTable, 2))

        <span style="color: #0000ff;">For</span> i=0 <span style="color: #0000ff;">to</span> UBound(mTestTable, 1)
            <span style="color: #0000ff;">For</span> j=0 <span style="color: #0000ff;">to</span> UBound(mTestTable, 2)
                DataArr(i,j) = mTestTable(i,j)
            <span style="color: #0000ff;">Next</span>
        <span style="color: #0000ff;">Next</span>
    <span style="color: #0000ff;">End</span> Sub
          <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>

Итак, мы вплотную подобрались к центральной части нашего функционала – вычитке данных из Excel источника.

За данную часть функционала отвечают два взаимосвязанных метода: GetArrayFromStore и LoadFromStore

Первый позволяет нам выгрузить вычитанные данные во внешний массив, минуя наш класс, а второй наоборот – инициирует наш класс вычитанными данными.

<span style="color: #008000;">\' @Documentation Extracts test data from the Excel store</span>
<span style="color: #008000;">\'------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> GetArrayFromStore(Arr(), TableName, StoreName)

    <span style="color: #0000ff;">Dim</span> Connection
    <span style="color: #0000ff;">Dim</span> i, j, fieldcount, rowsfetched
    <span style="color: #0000ff;">Dim</span> ArrTemp()
    <span style="color: #0000ff;">Dim</span> retcode
    <span style="color: #0000ff;">Dim</span> RepPath, BufStr

    <span style="color: #0000ff;">Set</span> Connection = CreateObject(<span style="color: #006080;">"ADODB.Connection"</span>)

    Connection.ConnectionString = <span style="color: #006080;">"DBQ="</span> + StoreName + _
        <span style="color: #006080;">";Driver={Microsoft Excel Driver (*.xls)}"</span> + _
        <span style="color: #006080;">";DriverId=790;FIL=excel 8.0;MaxBufferSize=2048"</span> + _
        <span style="color: #006080;">";MaxScanRows=8;PageTimeout=5;ReadOnly=1"</span> + _
        <span style="color: #006080;">";SafeTransactions=0;Threads=3;UserCommitSync=Yes"</span>

    Connection.Open
    <span style="color: #0000ff;">Set</span> ConnRs = CreateObject(<span style="color: #006080;">"ADODB.Recordset"</span>)

    ConnRs.CursorType = 3
    <span style="color: #0000ff;">Call</span> ConnRs.Open(<span style="color: #006080;">"select * from "</span> + TableName, Connection)

    fieldcount = ConnRs.Fields.Count
    <span style="color: #0000ff;">Redim</span> ArrTemp(fieldcount - 1, 0)

    <span style="color: #008000;">\'column names adding</span>
    <span style="color: #0000ff;">for</span> j=0 <span style="color: #0000ff;">to</span> fieldcount - 1
        ArrTemp(j,0) = ConnRs.Fields(j).Name
    <span style="color: #0000ff;">next</span>

    rowsfetched = 0
    ConnRs.MoveFirst()

    <span style="color: #0000ff;">While</span> <span style="color: #0000ff;">not</span> ConnRs.Eof
        rowsfetched = rowsfetched + 1
        <span style="color: #0000ff;">Redim</span> <span style="color: #0000ff;">Preserve</span> ArrTemp(fieldcount - 1, rowsfetched)

        <span style="color: #0000ff;">for</span> j=0 <span style="color: #0000ff;">to</span> fieldcount - 1
            ArrTemp(j, rowsfetched + i) = ConnRs(j).value
        <span style="color: #0000ff;">next</span>
        ConnRs.MoveNext()
    <span style="color: #0000ff;">Wend</span>

    Connection.Close

    <span style="color: #008000;">\'matrix transposition</span>
    <span style="color: #0000ff;">Redim</span> Arr(UBound(ArrTemp,2), UBound(ArrTemp,1))

    <span style="color: #0000ff;">for</span> i=LBound(ArrTemp,1) <span style="color: #0000ff;">to</span> UBound(ArrTemp,1)
        <span style="color: #0000ff;">for</span> j=LBound(ArrTemp,2) <span style="color: #0000ff;">to</span> UBound(ArrTemp,2)
            Arr(j,i) = ArrTemp(i,j)
        <span style="color: #0000ff;">next</span>
    <span style="color: #0000ff;">next</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

<span style="color: #008000;">\' @Documentation Loads test data from Excel store to the TestData object</span>
<span style="color: #008000;">\'-----------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> LoadFromStore (TableName, StoreName)
    <span style="color: #0000ff;">call</span> GetArrayFromStore(mTestTable, TableName, StoreName)
<span style="color: #0000ff;">End</span> Sub

Как вы можете заметить, ничего сверхъественного в реализации данного функционала нет. Более того очевидно, что с небольшими модификациями такую же процедуру можно применять для вычитки данных из любого ODBC источника.

Нам осталось добавить несколько сервисных методов, чтобы наш класс стал действительно удобным в использовании: GetCellByIndex, GetCellByName, ColumnCount, RowCount

 

<span style="color: #008000;">\' @Documentation Gets cell value by row-column indexes</span>
<span style="color: #008000;">\'------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> GetCellByIndex (RowIndex, ColumnIndex)
    GetCellByIndex = mTestTable(RowIndex+1, ColumnIndex)
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

<span style="color: #008000;">\' @Documentation Gets cell value by column name and row index</span>
<span style="color: #008000;">\'-------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> GetCellByName (ColumnName, RowIndex)
    <span style="color: #0000ff;">Dim</span> j

    <span style="color: #0000ff;">for</span> j = 0 <span style="color: #0000ff;">to</span> UBound(mTestTable,2)
        <span style="color: #0000ff;">If</span> (ColumnName = mTestTable(0,j)) <span style="color: #0000ff;">Then</span>
            <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">For</span>
        <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">if</span>
    <span style="color: #0000ff;">next</span>
    GetCellByName = mTestTable(RowIndex+1, j)
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

<span style="color: #008000;">\' @Documentation Returns number of columns into the test data table</span>
<span style="color: #008000;">\'------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> ColumnCount()
    ColumnCount = UBound(mTestTable, 2)
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

<span style="color: #008000;">\' @Documentation Returns number of rows into the test data table</span>
<span style="color: #008000;">\'---------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> RowCount()
    RowCount = UBound(mTestTable, 1) - 1
<span style="color: #0000ff;">End</span> Function

2.4. Со стороны тестового скрипта

Как же будет выглядеть минимально необходимый набор операций для инициализации нашего класса и вычитки данных на стороне скрипта.

<span style="color: #0000ff;">Dim</span> TestArr() ‘Dynamic array
<span style="color: #0000ff;">Dim</span> TestD

<span style="color: #0000ff;">Set</span> TestD = CreateTestData()
Сall TestD.LoadFromStore(<span style="color: #006080;">"NamedRange"</span>, <span style="color: #006080;">"C:\ExcelStore.xls"</span>)
Сall TestD.GetArrayFromStore(TestArr, <span style="color: #006080;">"NamedRange"</span>, <span style="color: #006080;">"C:\ExcelStore.xls"</span>)
‘<span style="color: #0000ff;">Call</span> TestD.GetArrayFromURL(TestArr, <span style="color: #006080;">"OrgChart@"</span>+ <span style="color: #006080;">"C:\ExcelStore.xls"</span>)
<span style="color: #0000ff;">Call</span> TestD.SetData(TestArr)

<span style="color: #0000ff;">Erase</span> TestArr

<span style="color: #0000ff;">Call</span> TestD.GetData(TestArr)

Print TestArr(1,1)
Print TestD.GetCellByIndex(2,2)
Print TestD.GetCellByName(<span style="color: #006080;">"Name"</span>, 2)
Print TestD.RowCount
Print TestD.ColumnCount

Обращаю ваше внимание, что массив (если вы решите выгружать данные из TestData объекта) должен быть динамическим, а не статическим.

Для более глубокого понимания функционала предлагаю пройтись по нему в режиме отладки и внимательно проконтролировать весь процесс.

Вы также наверняка заметили одну закомментированную функцию, я имею в виду GetArrayFromURL.

Я использую данную функцию для удобной реализации вложенных данных. В этом случае на уровне Excel каждая строка должна содержать дополнительную колонку, в которой хранится URL связанного массива данных.

clip_image010

Предлагаю вам реализовать данную функциональность самостоятельно.

3. Выводы

Возможно, кого-то, во время прочтения статьи, не покидало ощущение бесполезности реализации дополнительной функциональности имея стандартные средства работы с Eхcel. Частично я уже объяснял причины необходимости этого выше (Основные задачи). Но кроме этого хотелось бы еще добавить в копилку знаний несколько замечаний.

В силу особенностей моей специализации (функциональное автоматизированное тестирование) мне не удается работать с каким-то одним фреймворком. Тем не менее, всегда очень хочется использовать предыдущие наработки и обустроить свою работу максимально комфортно. Как оказалось это совсем не сложно и позволяет в итоге работать в привычных комфортных условиях, максимально используя уже проверенные схемы.

, ,

Universal class for data manipulations

0
by on April 11, 2009 at 07:05

Sergey Talalaev (SQAdotBY) has contributed this wonderful article on using a generic class for all your Excel data-operations needs. Thanks SQAdotBY, for you hard work, and willingness to share it with the QTP community! This article is copyrighted to Sergey.

A Russian translation is available here.

Content:

1.          Introduction.

2.          Excel data manipulation.

2.1.       Main goals

2.2.       Preliminary steps

2.3.       From functional library side.

2.4.       From the test script side.

3.          Conclusions.

1.   Introduction

I had previous experience with HP Mercury products, but only related to the previous product line – WinRunner. Naturally, I expected some kind of similarity in the script development and the whole framework organization. Therefore I’d like to note all detected interesting moments and provide my examples of some suitable functionality realization.

2.   Excel data manipulation

Working with WinRunner I got evidence that the standard realization of Excel data storage is not as flexible as I want. Therefore, starting my work with QTP, I’ve started to analyze the possibilities for migrating my Excel related library to QTP. And I was a little embarrassed about the lack of standard tools for direct DB manipulations.

 

2.1.    Main goals

 

Firstly let’s decide why it’s really useful for us.

I’ve always adhere the opinion that test data preparation process should be as easy as possible on one hand, and give ability to avoid errors on the other hand.

These hardly compatible goals can be perfectly realized by Excel data validation functionality, but it is perfectly destroyed after modification from QTP. Moreover, also destroyed are any custom formatting and without it well formatted Excel table changed into the chequered text.

 

Before modification by QTP:

clip_image002

 

After modification by QTP:

clip_image004

 

To avoid the problems I always use direct loading from Excel into arrays via standard ODBC functionality. This technique was successfully implemented for next test frameworks: Rational Robot, IBM Rational Functional Tester, WinRunner and I hope QTP. In addition to resolving all problems above we avoid serious restriction about using only one data table for one Excel sheet.

2.2.    Preliminary steps

 

There is a not so famous functionality in MS Excel, which allows separating important for user subset of cells into special structures. These structures are called “named ranges” and provide possibility of referencing to it via logical names. More over (and more significantly for us) these ranges become visible as a standard ODBC tables from external applications out of Excel.

So, for declaration chosen cell subset as a named range we need to perform next steps:

-       select all cells planned to be marked as named range

-       create named range via “Define name” dialog (Formulas > Define  Name)

 

clip_image006

 

or directly, entering name of the range into Navigation Bar.

 

clip_image008

 

To check correctness just created range – select all range cells and check value into

 Navigation Bar. It should be equal to range name instead of standard A1 notation.

 

It’s important to note one mandatory requirement if you are going to use named range functionality:

-       first row of your range should contains columns names, not the data

 

2.3.    From functional library side

 

After all preliminary steps, it’s only remains a little part of handwork, I mean coding.

QTP 9.5 supports objects and it’s reasonable to use this suitable functionality. Therefore all our code we embed into the class TestData.

 

I’d like to remind that QTP doesn’t see directly classes declared into external library files, especially because of this we need to have function that create class instance, in our case it’s CreateTestData

Besides that it’s a good idea to have possibility for initialization our class not only from Excel store but from arrays directly from the code. Especially for that additional two methods were implemented: SetData and GetData

.

 

<span style="color: #0000ff">Public</span> <span style="color: #0000ff">Function</span> CreateTestData ()
    <span style="color: #0000ff">Set</span> CreateTestData = <span style="color: #0000ff">new</span> TestData
<span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>
 
<span style="color: #0000ff">Class</span> TestData
          <span style="color: #0000ff">Private</span> mTestTable()
 
          <span style="color: #0000ff">Private</span> <span style="color: #0000ff">Sub</span> Class_Initialize()
          <span style="color: #0000ff">End</span> <span style="color: #0000ff">Sub</span>
 
          <span style="color: #0000ff">Private</span> <span style="color: #0000ff">Sub</span> Class_Terminate()
             <span style="color: #0000ff">Erase</span> mTestTable
          <span style="color: #0000ff">End</span> <span style="color: #0000ff">Sub</span>
 
          <span style="color: #008000">\' @Documentation Initiates TestData object with external data</span>
          <span style="color: #008000">\'-----------------------------------------------------------</span>
          <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Sub</span> SetData (DataArr())
                   <span style="color: #0000ff">Dim</span> i,j
 
                   <span style="color: #0000ff">ReDim</span> mTestTable(UBound(DataArr, 1), UBound(DataArr, 2))
                   <span style="color: #0000ff">For</span> i=0 <span style="color: #0000ff">to</span> UBound(DataArr, 1)
                             <span style="color: #0000ff">For</span> j=0 <span style="color: #0000ff">to</span> UBound(DataArr, 2)
                                      mTestTable(i,j) = DataArr(i,j)
                             <span style="color: #0000ff">Next</span>
                   <span style="color: #0000ff">Next</span>
          <span style="color: #0000ff">End</span> <span style="color: #0000ff">Sub</span>
 
 
          <span style="color: #008000">\' @Documentation Extracts data from TestData object</span>
          <span style="color: #008000">\'---------------------------------------------------</span>
          <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Sub</span> GetData(DataArr())
                   <span style="color: #0000ff">Dim</span> i,j
                   <span style="color: #0000ff">ReDim</span> DataArr(UBound(mTestTable, 1), UBound(mTestTable, 2))
                   <span style="color: #0000ff">For</span> i=0 <span style="color: #0000ff">to</span> UBound(mTestTable, 1)
                             <span style="color: #0000ff">For</span> j=0 <span style="color: #0000ff">to</span> UBound(mTestTable, 2)
                                      DataArr(i,j) = mTestTable(i,j)
                             <span style="color: #0000ff">Next</span>
                   <span style="color: #0000ff">Next</span>
          <span style="color: #0000ff">End</span> <span style="color: #0000ff">Sub</span>
          <span style="color: #0000ff">End</span> <span style="color: #0000ff">Class</span>

 

So we are now as much closer as possible to implementation the core functionality module – data loading from Excel. There are two closely interconnected methods responsible for that: GetArrayFromStore and LoadFromStore. First one allows us to extract loading data into external array not involving internal class store, second one vice versa – initiates our class by loaded data.

 

<span style="color: #008000">\' @Documentation Extracts test data from the Excel store</span>
<span style="color: #008000">\'------------------------------------------------------</span>
<span style="color: #0000ff">Public</span> <span style="color: #0000ff">Sub</span> GetArrayFromStore(Arr(), TableName, StoreName)
         <span style="color: #0000ff">Dim</span> Connection
         <span style="color: #0000ff">Dim</span> i, j, fieldcount, rowsfetched
         <span style="color: #0000ff">Dim</span> ArrTemp()
         <span style="color: #0000ff">Dim</span> retcode
         <span style="color: #0000ff">Dim</span> RepPath, BufStr
 
         <span style="color: #0000ff">Set</span> Connection = CreateObject(<span style="color: #006080">"ADODB.Connection"</span>)
 
         Connection.ConnectionString = <span style="color: #006080">"DBQ="</span> + StoreName + _
                                      <span style="color: #006080">";Driver={Microsoft Excel Driver (*.xls)}"</span> + _
                                      <span style="color: #006080">";DriverId=790;FIL=excel 8.0;MaxBufferSize=2048"</span> + _
                                      <span style="color: #006080">";MaxScanRows=8;PageTimeout=5;ReadOnly=1"</span> + _
                            <span style="color: #006080">";SafeTransactions=0;Threads=3;UserCommitSync=Yes"</span>
         Connection.Open
         <span style="color: #0000ff">Set</span> ConnRs = CreateObject(<span style="color: #006080">"ADODB.Recordset"</span>)
 
         ConnRs.CursorType = 3
         <span style="color: #0000ff">Call</span> ConnRs.Open(<span style="color: #006080">"select * from "</span> + TableName, Connection)
         fieldcount = ConnRs.Fields.Count
         <span style="color: #0000ff">Redim</span> ArrTemp(fieldcount - 1, 0)
 
         <span style="color: #008000">\'column names adding</span>
         <span style="color: #0000ff">for</span> j=0 <span style="color: #0000ff">to</span> fieldcount - 1
                   ArrTemp(j,0) = ConnRs.Fields(j).Name
         <span style="color: #0000ff">next</span>
 
         rowsfetched = 0
         ConnRs.MoveFirst()
 
         <span style="color: #0000ff">While</span> <span style="color: #0000ff">not</span> ConnRs.Eof
                   rowsfetched = rowsfetched + 1
                   <span style="color: #0000ff">Redim</span> <span style="color: #0000ff">Preserve</span> ArrTemp(fieldcount - 1, rowsfetched)
 
                   <span style="color: #0000ff">for</span> j=0 <span style="color: #0000ff">to</span> fieldcount - 1
                            ArrTemp(j, rowsfetched + i) = ConnRs(j).value
                   <span style="color: #0000ff">next</span>             
                   ConnRs.MoveNext()
         <span style="color: #0000ff">Wend</span>
 
         Connection.Close
 
         <span style="color: #008000">\'matrix transposition</span>
         <span style="color: #0000ff">Redim</span> Arr(UBound(ArrTemp,2), UBound(ArrTemp,1))
         <span style="color: #0000ff">for</span> i=LBound(ArrTemp,1) <span style="color: #0000ff">to</span> UBound(ArrTemp,1)
                   <span style="color: #0000ff">for</span> j=LBound(ArrTemp,2) <span style="color: #0000ff">to</span> UBound(ArrTemp,2)
                            Arr(j,i) = ArrTemp(i,j)
                   <span style="color: #0000ff">next</span>
         <span style="color: #0000ff">next</span>
<span style="color: #0000ff">End</span> <span style="color: #0000ff">Sub</span>
 
<span style="color: #008000">\' @Documentation Loads test data from Excel store to the TestData object</span>
<span style="color: #008000">\'-----------------------------------------------------------------------</span>
<span style="color: #0000ff">Public</span> <span style="color: #0000ff">Sub</span> LoadFromStore (TableName, StoreName)
         <span style="color: #0000ff">call</span> GetArrayFromStore(mTestTable, TableName, StoreName)
<span style="color: #0000ff">End</span> Sub

As you may see during our functional implementation we use only ordinary functionality and nothing from “braincrackers” equipment. Moreover it’s obviously that after small modifications we can use our class for direct loading from any other ODBC source.

We need to add several service methods to make our class really suitable: GetCellByIndex, GetCellByName, ColumnCount, RowCount

<span style="color: #008000">\' @Documentation Gets cell value by row-column indexes</span>
<span style="color: #008000">\'------------------------------------------------------</span>
<span style="color: #0000ff">Public</span> <span style="color: #0000ff">Function</span> GetCellByIndex (RowIndex, ColumnIndex)
         GetCellByIndex = mTestTable(RowIndex+1, ColumnIndex)
<span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>
 
<span style="color: #008000">\' @Documentation Gets cell value by column name and row index</span>
<span style="color: #008000">\'-------------------------------------------------------------</span>
<span style="color: #0000ff">Public</span> <span style="color: #0000ff">Function</span> GetCellByName (ColumnName, RowIndex)
         <span style="color: #0000ff">Dim</span> j
 
         <span style="color: #0000ff">for</span> j = 0 <span style="color: #0000ff">to</span> UBound(mTestTable,2)
                   <span style="color: #0000ff">If</span> (ColumnName = mTestTable(0,j)) <span style="color: #0000ff">Then</span> 
                            <span style="color: #0000ff">Exit</span> <span style="color: #0000ff">For</span> 
                   <span style="color: #0000ff">End</span> <span style="color: #0000ff">if</span>
         <span style="color: #0000ff">next</span>
 
         GetCellByName = mTestTable(RowIndex+1, j)
<span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>
 
<span style="color: #008000">\' @Documentation Returns number of columns into the test data table</span>
<span style="color: #008000">\'------------------------------------------------------------------</span>
<span style="color: #0000ff">Public</span> <span style="color: #0000ff">Function</span> ColumnCount()
         ColumnCount = UBound(mTestTable, 2)
<span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>
 
<span style="color: #008000">\' @Documentation Returns number of rows into the test data table</span>
<span style="color: #008000">\'---------------------------------------------------------------</span>
<span style="color: #0000ff">Public</span> <span style="color: #0000ff">Function</span> RowCount()
         RowCount = UBound(mTestTable, 1) - 1
<span style="color: #0000ff">End</span> Function

 

2.4.    From the test script side

 

What are the minimal required steps needed for using implemented functionality from test script side. As you may see from example below you just need to declare our object and initiate it by CreateTestData function.

 
<span style="color: #0000ff">Dim</span> TestArr()   ‘Dynamic array
<span style="color: #0000ff">Dim</span> TestD
 
<span style="color: #0000ff">Set</span> TestD = CreateTestData()
 
Сall TestD.LoadFromStore(<span style="color: #006080">"NamedRange"</span>, <span style="color: #006080">"C:\ExcelStore.xls"</span>)
Сall TestD.GetArrayFromStore(TestArr, <span style="color: #006080">"NamedRange"</span>, <span style="color: #006080">"C:\ExcelStore.xls"</span>)
‘<span style="color: #0000ff">Call</span> TestD.GetArrayFromURL(TestArr, <span style="color: #006080">"OrgChart@"</span>+ <span style="color: #006080">"C:\ExcelStore.xls"</span>)
 
<span style="color: #0000ff">Call</span> TestD.SetData(TestArr)
<span style="color: #0000ff">Erase</span> TestArr
 
<span style="color: #0000ff">Call</span> TestD.GetData(TestArr)
 
Print TestArr(1,1)
Print TestD.GetCellByIndex(2,2)
Print TestD.GetCellByName(<span style="color: #006080">"Name"</span>, 2)
Print TestD.RowCount
Print TestD.ColumnCount

I’d like to pay your attention to the following fact – array (if you decide to use extract data from TestData object) should be declared as dynamic, not static.

For deeper functional understanding I suggest to perform the code under debug mode and carefully check all the process.

 

You probably detected one commented function, I mean GetArrayFromURL.

I’ve use this function for suitable implementation nested data. In that case into your Excel tables additional column should be added, containing URL for nested data table (see picture below).

clip_image010

 

I suggest realizing this functionality yourself.

 

3.   Conclusions

 

Probably, during reading the article, you had ideas about uselessness additional functionality realization for Excel having standard ones. Partially I’ve described the main reasons of this activity above (see Main goals). But in addition to all said above I’d like to add into the moneybox several valuable notes.

Because of my specialty (functional test automation) and differences between the projects to be tested I have no ability to work constantly with one test framework. But in spite of this I always try to maximize reusing more successful techniques and practices. As you can see it’s not so difficult for some cases and gives ability to work in comfortable, habitual environment.

AdvancedQTP would like to thanks Sergey again, for all his hard work!

, ,

Display Records

0
by on April 12, 2008 at 05:26

The following example demonstrates how to display all records from a table. and loop thorough a record-set.

<span style="color: #0000ff;">Dim</span> oConn, oRst, oField
<span style="color: #0000ff;">Dim</span> sql

<span style="color: #0000ff;">set</span> oConn = CreateObject(<span style="color: #006080;">"ADODB.Connection"</span>)
oConn.Open <span style="color: #006080;">"QT_Flight32"</span>
<span style="color: #0000ff;">set</span> oRst = CreateObject(<span style="color: #006080;">"ADODB.recordset"</span>)
oRst.Open <span style="color: #006080;">"Select * from Orders"</span>, oConn
<span style="color: #0000ff;">Do</span> <span style="color: #0000ff;">Until</span> oRst.EOF
    <span style="color: #0000ff;">For</span> <span style="color: #0000ff;">each</span> oField <span style="color: #0000ff;">in</span> oRst.Fields
        Print oField.Name & <span style="color: #006080;">" = "</span> & oField.Value
    <span style="color: #0000ff;">Next</span>
    Print <span style="color: #0000ff;">String</span>( 20, <span style="color: #006080;">"-"</span> )
    oRst.MoveNext
<span style="color: #0000ff;">loop</span>

oRst.close
oConn.close

 

, ,