Wednesday, 18 July 2012

Test Automation – Data Driven Functional Decomposition Approach


Test Automation – Data Driven Functional Decomposition Approach
Version 1.0

This document describes the customize Test Automation - Data Driven Functional Decomposition Approach.

Milind Keer (milindkeer@gmail.com)
10/2/2010






Table of Contents








There are various standard ‘Functional Test Automation’ approaches/framework you can consider implementing in your project as per your project test requirements. Few examples are,
·         Key Word Driven Testing - This method uses the actual Test Case document developed by the tester using a spreadsheet containing special ‘Key-Words’. In this method, the entire process is data-driven, including functionality.
·         Data-Driven Testing - Data-driven testing is a framework where test input and output values are read from data files (datapools, ODBC sources, csv files, Excel files, DAO objects, ADO objects, and such) and are loaded into variables in captured or manually coded scripts. In this framework, variables are used for both input values and output verification values. Navigation through the program, reading of the data files, and logging of test status and information are all coded in the test script.
·         Modularity-Driven Testing - The test script modularity framework requires the creation of small, independent scripts that represent modules, sections, and functions of the application-under-test. These small scripts are then used in a hierarchical fashion to construct larger tests, realizing a particular test case.
In most of the projects it’s always not possible to follow one standard approach due to specific project requirements and you end up merging two approaches to meet the objective. In my last project we have done the same thing, we have merged multiple approaches and have designed new customized bespoke approach, we called it ‘Data Driven Functional Decomposition Approach’
This document details the design framework of this approach and the prototype.



The main concept behind the Functional Decomposition script development methodology is to break down the system to its components by reducing all test cases to their most fundamental tasks, and write User-Defined Functions, Reusable Business Functions and Sub-routine or Utility Functions which perform these tasks independently of one another.
In order to accomplish this, it is necessary to separate Data from Function. This would allow an automated test script to be written for a Business Function, using data-files to provide the both the input and the expected-results verification.
In the framework, the highest level is the Driver script, which is the engine of the test. The Driver Script contains a series of calls to one or more automated test scripts (‘Actions’).  The automated test scripts contain the test case logic, calling the Reusable Business Functions necessary to perform the application testing.  Utility functions are called as needed by Drivers, Automated Scripts and Business Functions.
·         Master Driver Scripts: Perform initialization (if required), then call the Automated Test Scripts in the desired order.
·         Automated Test Scripts: Perform the application test case logic using Reusable Business Functions
·         Reusable Business Functions: Perform specific Business Functions within the application
·         Subroutine or utility Functions: Perform application specific tasks required by two or more Business Functions and or independent utility such string manipulation, calculations etc.
·         User-Defined Functions: General, Application-Specific, and Screen-Access Functions.



Figure 1 : Typical Functional Decomposition Approach
In standard Functional Decomposition approach data would be maintained in multiple data files and/or one file per Test Case. There would be many numbers of data inputs and verifications to each data file and in most of the cases these are repetitive. For any change, maintaining these data inputs and that too in multiple files is very time consuming and tedious task. Imagine if you have 100 data files or even tabs how difficult would it be to maintain for single change.
Apart from this, even though we are breaking system into multiple reusable functions, one ‘Action’ script per test case would be required to make call to these series of reusable functions as per the script functional flow. If there are 1000+ test cases you would need to write 1000+ automated actions in QTP. It is understood that if there is any functionality change we would need to modify only relevant functions and this would be applicable to each and every test script which is fair enough but what if the flow itself change? What if one additional function would need to be added to the script flow? What if we need to make changes to the parameters which are being passed to reusable business functions? You would need to make all these updates in 1000+ automated actions and this would cost you a lot.
To mitigate all these challenges and to make automation suite maintenance free (or less maintenance) I have come up with new customize approach, i.e. Data Driven Functional Decomposition Approach.
The concept is same, the system would still be broken into multiple components, Reusable Business Functions, Utility Functions etc would be developed in the same manner. But we will not develop Test Case level Automated Test Scripts i.e. 1000+ test scripts mentioned in above section and data won’t be maintained at script level or business function level in multiple excels.
Instead of that only one single ‘Driver Data Sheet’ would be created to replace the ‘automated test scripts’ and ‘Data’ from the multiple excels.
This would act as an engine of the automated test suite. The driver sheet has been divided into following main sections,



Figure 2: Sample Driver Data Sheet
·         Execution Flag (Column B) – This column would indicate or would inform ‘Master Driver Script’ which test script to be executed and which to be ignored. Test scripts marked with ‘Y’ will be executed and rest all will be ignored.
·         Script Name (Column C) – All Test Case names. (There are manual test case names).
·         Function Names (Column D to H) – for Reusable Business Functions, Utility Functions in the desired order of the test script. E.g. Test Case –3 end to end flow would be completed by invoking Functions F1 ->F2->F8->F7->F3 in that particular order. The blank cell indicates the end of the flow.
·         Test Data Input Parameters (Column I to M) – place holder for all the input data parameters. All columns might not be relevant for every script, irrelevant data parameters can be kept blank. E.g. Field 2 might not be relevant for Test Case – 2.
A Master Driver Script will first read the data from ‘Driver Data Sheet’ and as per the execution flag set all relevant functions would be called one after another. All data fields are global and no need to pass on the values to any functions. The ‘Driver Data Sheet’ is nothing but a global sheet in QTP and hence script would be executed in iterative mode depending upon number of rows available in the ‘Driver Data Sheet’.
E.g. In above example (Figure 2), In first Iteration function ‘F1’ would be invoked and upon successful completion of function ‘F1’ function ‘F2’ would be invoked and so on… the last function invoked would be ‘F4’ which is again end of the first iteration and/or end of the first case. In second iteration again call the mentioned functions would be called in the given order, In third iteration no single function would be called because execution flag has been set to ‘N’, script will skip this and move on to fourth iteration… and so on….
Here every single script is being called as new iteration hence data would automatically overwritten i.e. every single iteration is a fresh execution and there won’t be any memory blockage.
I have built a sample prototype to explain the above approach. This is prototype is based on ‘Flight’ application.

<Under Development>

As of now, please refer Appendix for Sample Code…
If you need any further assistance you can reach me on milindkeer@gmail.com






******************************************************************************
Call getdata()

For i=2 to  rowCount
    If  vaData (i,2) = "Y" Then

                        reporter.ReportEvent micDone, vaData (i,3), vaData (i,3) & "  - has been called"

                        For j=1 to colCount
                                        functionname =  vaData (i,j)
                                        Select Case functionname
                                                        Case "F1"
                                                                        status = F1()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If
       
                                                        Case "F2"
                                                                        status = F2()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If   
       
                                                        Case "F3"
                                                                        status = F3()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If
                                                                                       
                                                         Case "F4"
                                                                        status = F4()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If
                                                                       
                                                        Case "F5"
                                                                        status = F5()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If
       
                                                        Case "F6"
                                                                        status = F6()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If
       
                                                        Case "F7"
                                                                        status = F7()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If
       
                                                        Case "F8"
                                                                        status = F8()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If
       
                                                        Case "F9"
                                                                        status = F9()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If
       
                                                        Case "F10"
                                                                        status = F10()
                                                                        If  status = "Fail" Then
                                                                                        call LogReport("Fail", functionname)  
                                                                                        Exit For
                                                                        Else
                                                                                         call LogReport("Pass", functionname)  
                                                                        End If
                                                                       
                                        End Select
       
                                         
                        Next

        Else
                        'Exit For
        End If
Next

******************************************************************************