Search code examples
javaunit-testingtestingautomated-teststestng

How to run the same test on values from a list of Objects and on nested lists?


I have generated a list of Objects of type Household Object, that has a String name and a List colours, from a CSV file. I want to run the same tests on every element in my list of Household objects. So:

@Test(priority = 1) 
test1(String name){
assert something
}

@Test(priority =2)
test2(String colour){
assert something
}

So if I have a Household object: Name = Chair, colours = [pink,yellow,blue] I want to run test 1 with Name 'chair' once, and then I want to run Test 2 for every colour in the list. I want to do this for every object in my list of elements.

I am trying to use data provider:

@DataProvider
    public  Object[][] data() throws CsvValidationException, IOException, URISyntaxExcepton {
        CVS reader = new CVS();
        houseHoldObjects = reader.getHouseHoldObjects();
        
        return new Object[][] {
            {
            }
        };

    }

However I don't understand how I can do this for every object in the list, and for every colour in each object.


Solution

  • TestNg has features to implement such advanced configuration.

    However you'll need to apply the different features combo with accuracy.

    So let's define the request explicitly:

    1. We have test class with 2 test methods.
    2. We want to run this class multiple times based on particular dataset record.
    3. In the class we have 1 method which also has to be executed multiple times within a single class dataset.

    Solution:

    1 To parametrize the whole class we'll use TestNg @Factory feature in combination with @DataProvider.

    For that purpose we'll create static @DataProvider which will return the data in the described manner: String name and List of colors - provideTestClassData.

    Note, I'll not solve CSV parsing/transform issue, I assume it'll be solved separately.

    2 Also I'll create class constructor with the required properties initialisation.

    3 For test2 I'll add @DataProvider for colors - provideColours.

    package org.example;
    
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Factory;
    import org.testng.annotations.Test;
    
    public class ColoredFurnitureTest {
    
      final String name;
      final List<String> colours;
    
      @Factory(dataProvider = "provideTestClassData")
      public ColoredFurnitureTest(String name, List<String> colours) {
        this.name = name;
        this.colours = colours;
      }
    
      @Test(priority = 1)
      public void test1() {
        System.out.println("Do test1 for " + name);
        //assert something
      }
    
      @Test(priority = 2, dataProvider = "provideColours")
      public void test2(String colour) {
        System.out.println("Do test2 for " + name + "with color " + colour);
        //assert something
      }
    
      @DataProvider
      public Object[][] provideColours() {
        List<Object[]> data = new ArrayList<>();
        for (String c : colours) {
          data.add(new Object[]{c})
        }
        data.toArray();
      }
    
      // assume CSV data will be parsed somehow to follow next structure
      @DataProvider
      public static Object[][] provideTestClassData() {
        return new Object[]{
            new Object[]{"Chair", Arrays.asList("pink", "yellow", "blue")},
            new Object[]{"Table", Arrays.asList("black", "white", "green")},
            new Object[]{"Closet", Arrays.asList("blue", "orange")},
        };
      }
    

    Run the whole class tests one-by-one per dataset

    If run the class without any additional configuration the run order will be next:

    Do test1 for Chair
    Do test1 for Table
    Do test1 for Closet
    Do test2 for Chair with color pink
    Do test2 for Chair with color yellow
    Do test2 for Chair with color blue
    Do test2 for Table with color black
    Do test2 for Table with color white
    Do test2 for Table with color green
    Do test2 for Closet with color blue
    Do test2 for Closet with color orange
    

    To run all class methods one-by one per dataset we need to enable group-by-instances="true" testNg feature. This can be defined in TestNg suite xml.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
    <suite name="ColoredFurnitureTest Suite">
        <test name="ColoredFurnitureTest" group-by-instances="true">
            <classes>
                <class name="org.example.ColoredFurnitureTest"/>
            </classes>
        </test> <!-- Test -->
    </suite> <!-- Suite -->
    

    if run with this option, the order will be next:

    Do test1 for Chair
    Do test2 for Chair with color pink
    Do test2 for Chair with color yellow
    Do test2 for Chair with color blue
    Do test1 for Table
    Do test2 for Table with color black
    Do test2 for Table with color white
    Do test2 for Table with color green
    Do test1 for Closet
    Do test2 for Closet with color blue
    Do test2 for Closet with color orange