Search code examples
testngplaywright

In Playwright + testNG how can I specify that a test case should run with x user roles, and for each user role with y data?


I don't find any approach for my usecase. Let's say that a testcase needs to be executed with 2 roles, and for each role there should be 2 test run with different data.

    @Test (dataProviderClass = roles.class, dataProvider = roles.SUPERADMIN_ADMIN)
    @Parameters({"data1", "data2"})
    public void myTestCase () {
    
        ...
    }

SUPERADMIN and DATA1, SUPERADMIN and DATA2, ADMIN and DATA1, ADMIN and DATA2

But this is not working since parameters should be set in xml file and multiple dataProviders are not supported.

Any high level help would be appreciated about how to approach this problem.


Solution

  • TestNG does not let you tie a single test method to multiple data providers.

    That being said, here's a sample that shows how to make your data provider dynamic enough such that it modifies itself based on the parameter passed from a suite file.

    public record Movie(String name) {}
    
    public record Book(String name) {}
    
    import org.testng.ITestNGMethod;
    import org.testng.annotations.CustomAttribute;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Test;
    import org.testng.xml.XmlClass;
    import org.testng.xml.XmlInclude;
    import org.testng.xml.XmlTest;
    
    import java.util.List;
    import java.util.Map;
    import java.util.Optional;
    
    public class SampleTestCase {
    
        private static final Object[][] defaultData = new Object[][]{
                {true},
                {false}
        };
    
        private static final List<Object> books = List.of(
                new Book("Next Generation Java Testing"),
                new Book("Head First Java")
        );
        private static final List<Object> movies = List.of(
                new Movie("KungFu Panda"),
                new Movie("The Last Air Bender")
        );
    
        private static final Map<String, List<Object>> dataStore = Map.of(
                "data_set1", books,
                "data_set2", movies
        );
    
        @Test(attributes = {
                @CustomAttribute(name = "id1") // Hinting that this method is to get a parameter named "id1" from xml
        }, dataProvider = "dp")
        public void testMethod1(Book id) {
            System.err.println("testMethod1() Book = " + id);
        }
    
        @Test(attributes = {
                @CustomAttribute(name = "id2") // Hinting that this method is to get a parameter named "id2" from xml
        }, dataProvider = "dp")
        public void testMethod2(Movie id) {
            System.err.println("testMethod2() Movie = " + id);
        }
    
        @Test(dataProvider = "dp")
        public void testMethod3(boolean flag) {
            System.err.println("Flag = " + flag);
        }
    
        @DataProvider(name = "dp")
        public Object[][] getData(ITestNGMethod method) {
            CustomAttribute[] attributes = method.getAttributes();
            if (attributes.length == 0) {
                //No annotation was found. So send back default data provider values
                return defaultData;
            }
    
            //Now let's try to read the parameters that were set
    
            Map<String, String> localParams = extractParameters(method);
    
            for (CustomAttribute attribute : attributes) {
                boolean present = localParams.containsKey(attribute.name());
                if (!present) {
                    continue;
                }
                String whichDataSet = localParams.get(attribute.name());
                List<Object> datum = Optional.ofNullable(dataStore.get(whichDataSet))
                        .orElseThrow(() -> new IllegalArgumentException("No data found for " + whichDataSet));
                return datum.stream()
                        .map(it -> new Object[]{it})
                        .toArray(size -> new Object[size][1]);
            }
    
            //if we are here, then send back another default data
            return defaultData;
        }
    
        private static Map<String, String> extractParameters(ITestNGMethod method) {
            Class<?> requiredClass = method.getRealClass();
            String requiredMethod = method.getMethodName();
            XmlTest currentTest = method.getXmlTest();
            XmlClass filter = new XmlClass(requiredClass);
            XmlInclude result = null;
            for (XmlClass each : currentTest.getXmlClasses()) {
                if (!each.equals(filter)) {
                    //If the class name does not match, then skip to the next class in the suite file
                    continue;
                }
                Optional<XmlInclude> found = each.getIncludedMethods().stream()
                        .filter(it -> it.getName().equalsIgnoreCase(requiredMethod))
                        .findFirst();
                if (found.isPresent()) {
                    //if we found a method that matches the method we are searching for, then remember it
                    // and break the loop
                    result = found.get();
                    break;
                }
            }
            //Now try to extract out the local parameters associated with the <include> tag
            //If there were none found, then error out with an exception
            return Optional.ofNullable(result)
                    .map(XmlInclude::getLocalParameters)
                    .orElseThrow(() -> new IllegalArgumentException(requiredClass.getName() + "." + requiredMethod + "() combo not found"));
        }
    }
    

    Here's the suite file that we are going to be working with

    <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
    <suite name="2980_suite" verbose="2">
        <test name="2980_test" verbose="2">
            <classes>
                <class name="com.rationaleemotions.so.qn78064092.SampleTestCase">
                    <methods>
                        <include name="testMethod1">
                            <parameter name="id1" value="data_set1"/>
                        </include>
                    </methods>
                </class>
                <class name="com.rationaleemotions.so.qn78064092.SampleTestCase">
                    <methods>
                        <include name="testMethod2">
                            <parameter name="id2" value="data_set2"/>
                        </include>
                    </methods>
                </class>
                <class name="com.rationaleemotions.so.qn78064092.SampleTestCase">
                    <methods>
                        <include name="testMethod3"/>
                    </methods>
                </class>
            </classes>
        </test>
    </suite>
    

    Here's the execution output:

    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
    SLF4J: Defaulting to no-operation (NOP) logger implementation
    SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
    ...
    ... TestNG 7.9.0 by Cédric Beust ([email protected])
    ...
    
    testMethod1() Book = Book[name=Next Generation Java Testing]
    testMethod1() Book = Book[name=Head First Java]
    testMethod2() Movie = Movie[name=KungFu Panda]
    testMethod2() Movie = Movie[name=The Last Air Bender]
    Flag = false
    Flag = true
    PASSED: com.rationaleemotions.so.qn78064092.SampleTestCase.testMethod2(Movie[name=KungFu Panda])
    Test Attributes: <id2, []>
    
    PASSED: com.rationaleemotions.so.qn78064092.SampleTestCase.testMethod2(Movie[name=The Last Air Bender])
    Test Attributes: <id2, []>
    
    PASSED: com.rationaleemotions.so.qn78064092.SampleTestCase.testMethod3(false)
    PASSED: com.rationaleemotions.so.qn78064092.SampleTestCase.testMethod1(Book[name=Next Generation Java Testing])
    Test Attributes: <id1, []>
    
    PASSED: com.rationaleemotions.so.qn78064092.SampleTestCase.testMethod1(Book[name=Head First Java])
    Test Attributes: <id1, []>
    
    PASSED: com.rationaleemotions.so.qn78064092.SampleTestCase.testMethod3(true)
    
    ===============================================
        2980_test
        Tests run: 3, Failures: 0, Skips: 0
    ===============================================
    
    
    ===============================================
    2980_suite
    Total tests run: 6, Passes: 6, Failures: 0, Skips: 0
    ===============================================
    
    
    Process finished with exit code 0