Forum Discussion

mikev's avatar
mikev
Contributor
13 years ago

Data Driven Projects, Tests & Frameworks

New customer of TC, migrating tests from QTP.  So far, so good, but I'd like a little review by folks who have been using the tool for quite sometime.  I'll try and be as clear as I can, but keep in mind that I'm new to the tool, and I may not know the correct TC terminology.



Let's say that we have an application (doesn't matter what kind really, but in my case it's a Java Application), and that application has a main frame with 6 tabs.  Each tab has MANY (20+) objects on it.  A single instance of a test will basically launch the application, set all the parameters (by each tab) save the "instance", run the calculations, and finally comparing the output files.  



This kind of test is a prime candidate to make use of the data-driven aspect.  Since there are so many tabs, and there are so many possible parameters, I have an excel spreadsheet, that has 6 sheets contained in it - and each sheet corresponds with the appropriate application tab.



After looking at the Help, etc, I can easily create the driver and loop through every row for each sheet.  That's the easy part.  The problem that I have is that each iteration must use the same row from every sheet and every application tab.  I certainly could make one huge spreadsheet, and one huge test script, but based upon my testing experience, that's not such a great idea.  A nightmare to maintain.  This might be representative of a single iteration as I see it:



tab1 -> sheet1 -> row1

tab2 -> sheet2 -> row1

tab3 -> sheet3 -> row1

tab4 -> sheet4 -> row1

tab5 -> sheet5 -> row1

tab6 -> sheet6 -> row1



For the next iteration, this would be the flow:

tab1 -> sheet1 -> row2

tab2 -> sheet2 -> row2

tab3 -> sheet3 -> row2

tab4 -> sheet4 -> row2

tab5 -> sheet5 -> row2

tab6 -> sheet6 -> row2



The rest of the iterations would be the same, with only the row incrementing.  I'm not sure how to do that, since my research (so far) only provides for EOF for the Driver.  I don't know of any way to get the current row or number of rows, or how to set the row.  I'm not even sure if this is the right way to attack this problem, but it worked very well using QTP.



Now, in addition to the above problem, I'm also a little confused about the project layout and the framework of the tests themselves.  For example, I'd like to have a set of scripts that do the following:



Startup - Launches and initializes the application.  This should only happen once.

Tab1 - Fills out the parameters on tab1.  This should happen for every instance of the datasheet.

Tab2 - Fills out the parameters on tab2.  This should happen for every instance of the datasheet.

Tab3 - Fills out the parameters on tab3.  This should happen for every instance of the datasheet.

Tab4 - Fills out the parameters on tab4.  This should happen for every instance of the datasheet.

Tab5 - Fills out the parameters on tab5.  This should happen for every instance of the datasheet.

Tab6 - Fills out the parameters on tab6.  This should happen for every instance of the datasheet.

Calculate - Creates the output files.  This should happen for every instance of the datasheet.

Comprae - compares the output files against a baseline.  This should happen for every instance of the datasheet.

Teardown - kills the application.  This should only happen once.



So how would you structure this in the Project Suite?  My Project Suite is named after the AUT.  The Project is what kind of tests will be there.  For this example, I use the term Scenario for my Project, as this kind of tests is what we define as a scenario test.  Now, I can have multiple scripts (obviously) in the Scripts "item" inside the Scenario Project.  But I'm not sure that's what is best, given what I've explained above.  Should each "tab" be it's own project?  I'm just not sure what the best practice is to do what I'm trying to do.



Anyway, sorry for the length, I'm trying to talk with someone in support, but no response yet.  I don't think I'm the only person who does this kind of testing, and I'm sure someone has solved this problem.



Thoughts?
  • Hi Mike,


    The problem that I have is that each iteration must use the same row from every sheet and every application tab.


    You can do this with TestComplete, but you'll have to control the iteration yourself. I'd suggest that you create a driver for each tab and iterate through them manually, that is, something like this --


    While Not Driver1.EOF

       ' Do Something

      Driver1.Next

      Driver2.Next

      ...

      Driver6.Next

    WEnd


    If you are using keyword tests and the Data-Driven Loop operation, you need to create and control Driver2..Driver6, as driver 1 will be controlled by the operation. To call the Next method, you can use the Call Object Method operation.


    I'm also a little confused about the project layout and the framework of the tests themselves


    According to the information you provided, I don't think you have to create several projects.

    I'd suggest that you have your tests in one project. This way, it will be easier to maintain the NameMapping repository, for instance. You can create several tests, organize them into folders (or script units) for easier management, etc. You can create an individual test for each step, most likely, you will have something like this --


    1: Startup - A test that will perform initialization actions.

    2: TestTabs - This is the test that will

                            * Read data from worksheets in a loop

                            * For each data row, it will

                                  - simulate user actions on the tabs (fill the tabs 1..6);

                                  - output data to a file;

                                  - Compare - Performs the needed verifications. 

                                  Each of these substeps can be a script routine or keyword test.

                            * Call Next to proceed to the next data row.

    3: Teardown - A test that will perform finalization actions.


    You can create test items for tests 1, 2 and 3 on the Test Items page of the project editor.


     

  • Alex,



    Thanks for the reply.  I've given your response a bit of thought, and I kind of started down the path that you've suggested.  I was anticipating having to control the iteration, (that's why I made another post about Driver.setRow, Driver.getRow, etc.) but that technology doesn't exist within TC.



    Regarding to what you said here:



    While Not Driver1.EOF

       ' Do Something

        Driver1.Next

        Driver2.Next

         ...

      Driver6.Next

    Wend



    I *think* I'll still have a problem when I get to the last driver.  Here's what I mean:



    while (!Driver1.EOF){

      //Fill out parameters for tab1

         while(!Driver2.EOF){

        //Fill out paramters for tab2

          while(!Driver3.EOF){

          //Fill out parameters for tab3

          ..........

          ..........

              while (!Driver6.EOF){

              //Here is where the problem lies (I think).  When I get here, ALL rows for Driver 6 must be run.  There isn't a way that I know of that can tell TC to only "execute" one row and then move back to Driver1 and perform Driver1.Next();
  • I think I might be able to do what I want to do by accessing Excel directly.  It will be kind of like writing my own DDT driver.  Other than that, I'm out of ideas.
  • Hi Mike,


    Here's what I mean:


    while (!Driver1.EOF){

      //Fill out parameters for tab1

         while(!Driver2.EOF){

        //Fill out parameters for tab2

          while(!Driver3.EOF){

          //Fill out parameters for tab3

          ..........

          ..........

              while (!Driver6.EOF){

              //Here is where the problem lies (I think).  When I get here, ALL rows for Driver 6 must be run.


    As far as I understood, you have the same number of rows in each of the six datasheets, and you need to go through the sheets synchronously, right? In this case, you need only one while loop. If you want to control other drivers for EOF (just in case the test data is invalid), you can check them in the same "while" condition --


    while(!Driver1.EOF() && !Driver2.EOF()... )

    {

         // Your test goes here...


         Driver1.Next();

         Driver2.Next();

         ..

         Driver6.Next();

    }


    Does it work for you? Or am I missing something in your description?..

  • Alex, I'll have to try and code that one up - it's not something I considered, but it does look promising.  If it works, it should be a much easier solution than to write my own driver for Excel.  Thanks for the tip, I'll post my findings here.



    One quick question though - your solution looks like it will work if all of the test code (as far as manipulating the objects go) are in one script under the script tree.  What if I had one script for each tab?  How can I move back and forth there?  I haven't researched it yet, I just thought of that as I was typing this response.
  • your solution looks like it will work if all of the test code (as far as manipulating the objects go) are in one script under the script tree.  What if I had one script for each tab?  How can I move back and forth there?  I haven't researched it yet, I just thought of that as I was typing this response.




    My "pseudo-code" was not clear enough, sorry :-). Of course, you can create individual script routines to work with each of your tabs and call these routines from your "main" test. Moreover, I'd even recommend that you do this as in this case, the test logic will be clearer, and it will be easier to maintain tests. You can also organize your script routines in several units for easier management.

    As you will use DDT drivers in different script routines, you can use global script variables to store the driver objects. The entire script structure can look like this:


    // Define global variables

    var Driver1, Driver2, ... Driver6;


    // Initialize drivers

    function InitDrivers()

    {

      ...

    }


    // Simulate user actions on tabs

    function TestTab1()

    {

      ...

    }


    function TestTab2()

    {

      ...

    }


    // Perform finalization actions

    function Finalize()

    {

    }


    function Main()

    {

      InitDrivers();


      while (!Driver1.EOF() && !Driver2.EOF() ...)

      {

         TestTab1();

         TestTab2();

         ...

         Driver1.Next();

         Driver2.Next();

         ..

      }

     

      Finalize();

    }

  • OK Alex, I'm back on this problem.  I'll post up what I figure out.
  • This solution will work nicely.  Might even be a fairly good case for folks moving from QTP to TC.  This particular code does cause me some problems, but I'll post another thread for that.



    Anyway, here is the Controller code:



    //USEUNIT commonlib

    //USEUNIT applib

    //USEUNIT ScriptUnit1

    //USEUNIT ScriptUnit2

    //USEUNIT ScriptUnit3

    //USEUNIT ScriptUnit4

    //USEUNIT ScriptUnit5     

    //USEUNIT ScriptUnit6

    //USEUNIT ScriptUnit7

    //USEUNIT ScriptUnit8

    //USEUNIT ScriptUnit9

    //USEUNIT ScriptUnit10

    //USEUNIT ScriptUnit11



    function Controller() {



    //Get the test directory so we know where our datasheet is.

    var testDir = Project.Path;

    var testData = testDir + "\\Default.xls";



    //Set up the data drivers, specific to the sheet we want to use.

    var Sheet1 = DDT.ExcelDriver(testData, "ScriptUnit1");

    var Sheet2 = DDT.ExcelDriver(testData, "ScriptUnit2");

    var Sheet3 = DDT.ExcelDriver(testData, "ScriptUnit3");

    var Sheet4 = DDT.ExcelDriver(testData, "ScriptUnit4");

    var Sheet5 = DDT.ExcelDriver(testData, "ScriptUnit5");

    var Sheet6 = DDT.ExcelDriver(testData, "ScriptUnit6");

    var Sheet7 = DDT.ExcelDriver(testData, "ScriptUnit7");

    var Sheet8 = DDT.ExcelDriver(testData, "ScriptUnit8");

    var Sheet9 = DDT.ExcelDriver(testData, "ScriptUnit9");

    var Sheet10 = DDT.ExcelDriver(testData, "ScriptUnit10");

    var Sheet11 = DDT.ExcelDriver(testData, "ScriptUnit11");



    //Since all sheets must have the same number of rows, we can use the

    //intial sheet as the EOF check.  If this sheet is EOF, all of them are.

    while (!Sheet1.EOF()) {



      //First thing we need to do is get the Run Flag.  If it's 'N' we are not

      //running this iteration.

      var RUN_FLAG = Sheet1.Value("RunFlag");

      var myVar = Sheet1.Value("Val");

      if (RUN_FLAG == "N") {

        Log.Message("Skipping this iteration: " + Sheet1.Value("Val"));  

      }

      else {

     

      //Write a log so that we know which row we are on.

      Log.Message("**************     Running test " + myVar  + " **************");

             

      //Set the ScriptUnit1 values.

      ScriptUnit1Tab(Sheet1);

     

      //Set the ScriptUnit2 values.

      ScriptUnit2Tab(Sheet2);

     

      //Set the ScriptUnit3 values.

      ScriptUnit3Tab(Sheet3);

     

      //Set the ScriptUnit4 values.

      ScriptUnit4Tab(Sheet4);

     

      //Set the ScriptUnit5 values.

      ScriptUnit5Tab(Sheet5);



      //Set the ScriptUnit6 values.

      ScriptUnit6Tab(Sheet6);

       

      //Set the ScriptUnit7 values.

      ScriptUnit7Tab(Sheet7);

     

      //Set the ScriptUnit8 values.

      ScriptUnit8Tab(Sheet8);

     

      //Set the ScriptUnit9 values.

      ScriptUnit9Tab(Sheet9);

     

      //Set the ScriptUnit10 values.

      ScriptUnit10Tab(Sheet10);



      //Set the ScriptUnit9 values.

      ScriptUnit11Tab(Sheet11);

     

        }

        //Increment all rows for every sheet.

        Sheet1.Next();

        Sheet2.Next();

        Sheet3.Next();

        Sheet4.Next();

        Sheet5.Next();

        Sheet6.Next();

        Sheet7.Next();

        Sheet8.Next();

        Sheet9.Next();

        Sheet10.Next();

        Sheet11.Next();

    }



    }  // End of Controller



    And here is an example script unit:



    //USEUNIT commonlib

    //USEUNIT applib



    function ScriptUnit1Tab(Driver){



    //Set up our local variables from the Sheet1 sheet.  

    var var1 = Driver.Value("var1");

    var var2 = Driver.Value("var2");

    var var3 = Driver.Value("var3");



    //Function calls to do things specific to the data in the sheet.



    }//End of function

  • Mike,


    Thank you for sharing your solution.

    Nice to hear that everything works fine. :-)

  • Thanks for your help Alex.  Hopefully the solution that I posted is clear enough where people can understand it.  I think it might be a good example for folks moving over from QTP to replace Actions.