Parameterization Annotation In TestNg: Like TestNG Groups, parametrization is also an essential feature of TestNG. This feature allows us to run a test case multiple times with different input data and validate the values. We can use this feature with testNG by using the @Parameters annotation.
In our previous post, we shared the TestNg Framework Tutorials. We recommend you go through those articles to learn more about TestNG.
There are different ways are there by which we can achieve the parametrization, that is:
- Through tesng.xml file
- Through DataProviders
In this post, we are going into detail about parametrization with the testng.xml file.
Why We Parametrized Methods of TestNG?
Most web applications require a login because users are categorized into different types like admin, user, and author; each has responsibility for a different operation.
So, creating a separate login method for different users’ roles is not recommended. Instead, we can create one parametrized method by using parameterization, which receives the user credentials of different users and logs in.
Parameterization With @Parameters
The @Parameters annotation can be used for any of the @Before, @After, @Factory, and @Test annotated methods. It can be used to initialize variables and use them in a class, test, or maybe for the whole test execution.
Let’s Create a Simple parameterized method and see how the Parameterization concept will work with TestNG:
package Parameters; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class ParametersThroughXML { /* We need to add Parameters annotation to read value from testng xml. Note here that * attribute name passed in Parameters must be same as testng xml. */ @Parameters({“confBeforeParameter”}) @BeforeMethod public void configurationBeforeMethod(String confBeforeParameter) { System.out.println(“Paramters for before configuration method: “+confBeforeParameter); } @Parameters({“testParameters1″,”testParameters2”}) @Test public void testMethod(String testParameters1, String testParameters2) { System.out.println(“Paramters one for test method: “+testParameters1); System.out.println(“Paramters two for test method: “+testParameters2); } @Parameters({“confAfterParameter”}) @AfterMethod public void configurationAfterMethod(String confAfterParameter) { System.out.println(“Paramters for after configuration method: “+confAfterParameter); } }
As in the class, you can see that with the annotation, we have used another annotation, which is @Parameters, by which we can receive the input data passed from the textng.xml file and send that input data to the particular method.
Note:
- The @Parameters annotation is used to read the parameters from the testng.xml file.
- The attribute name you mentioned in your TestNG class should be the same as in the testng.xml file.
Now, let us create the testng.xml file with the parameter tag.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="Suite"> <test thread-count="5" name="Test"> <parameter name="confBeforeParameter" value="confBeforeParameter" /> <parameter name="testParameters1" value="testParameters1" /> <parameter name="testParameters2" value="testParameters2" /> <parameter name="confAfterParameter" value="confAfterParameter" /> <classes> <class name="com.softwaretestingo.testng.parameterization.PassingParametersThroughXML" /> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
Output:
When you run the above XML file, the output will be
Paramters for before configuration method: confBeforeParameter Paramters one for test method: testParameters1 Paramters two for test method: testParameters2 Paramters for after configuration method: confAfterParameter =============================================== Suite Total tests run: 1, Passes: 1, Failures: 0, Skips: 0 ===============================================
Difference Between “Parameters” and “Parameter” in TestNG
If you see the above class or XML file, you will see “@Parameters” and “parameter.” We have used the “@Parameters annotation,” which allows us to create the parametrized method in the testNg class. We are using the parameter tag in the XML file to send the test data from the XMl file to the testNG class.
How to Use Constructor with @Parameter Annotation
Previously, we have seen that if we want to pass input data to any test method, we use @parameters annotation with the test method. But it’s not a good programming practice to use @parameters annotation with every method.
Disadvantages Of Using @Parameters Annotation in Every Test Method
- You have to use the same @Parameters annotation with each parametrized method.
- If so many methods use the same parameter, then you must mention the parameter name with @parameters annotation on all those methods. So it is consuming most of your time.
- If your requirement is changed and you need to add or remove the parameter from the method, then you need to update the @parameters annotation and the method signature.
So, to overcome these disadvantages, we can use the constructor concept of Java here, by which we can initialize all the parameters at once with the values, and the method can use those values directly.
Let’s see a simple program to understand this:
package Parameters; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class ParametersExampleAtClonstructor { // List of parameters String p1; String p2; String p3; String p4; // Accept all parameters in Constructor from testng.xml @Parameters({"confBeforeParameter","testParameters1","testParameters2","confAfterParameter"}) public ParametersExampleAtClonstructor(String s1, String s2, String s3, String s4) { // Initialize all parameters System.out.println("in constructor"); p1=s1; p2=s2; p3=s3; p4=s4; } // Use whatever parameters are needed in methods // Note here I am not using any parameter in method signature and Parameters annotation @BeforeMethod public void configurationBeforeMethod() { System.out.println("Paramters for before configuration method: "+p1); } @Test public void testMethod() { System.out.println("Paramters one for test method: "+p2); System.out.println("Paramters two for test method: "+p3); } @AfterMethod public void configurationAfterMethod() { System.out.println("Paramters for after configuration method: "+p4); } }
Testng.xml File
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="Suite"> <test thread-count="5" name="Test"> <parameter name="confBeforeParameter" value="confBeforeParameter" /> <parameter name="testParameters1" value="testParameters1" /> <parameter name="testParameters2" value="testParameters2" /> <parameter name="confAfterParameter" value="confAfterParameter" /> <classes> <class name="com.softwaretestingo.testng.parameterization.PassingParametersWithClonstructor" /> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
Output:
The Output Will Be
Values Assigned Through Constructor Paramters for before configuration method: confBeforeParameter Paramters one for test method: testParameters1 Paramters two for test method: testParameters2 Paramters for after configuration method: confAfterParameter =============================================== Suite Total tests run: 1, Passes: 1, Failures: 0, Skips: 0 ===============================================
Doing this, if you want to add some parameter or remove a parameter, then there is no need to change the @parameters annotation or method signature. You can modify it in the constructor, affecting all the places.
How do you pass parameters at the test method level in TestNG?
Earlier in this post, we have seen how you can pass the data to a test method from a testng.xml as suite and test level. But if more parameters are mentioned in the testng.xml file, it’s too difficult to handle and identify which parameters are assigned to which method.
So, to overcome this type of situation, TestNg provides a feature that allows us to pass parameters at the individual method level. By this, we can put the related parameters of a method near that. But we are giving the value from the testng.xml file a bit differently.
Sample TestNg class:
package Parameters; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class ParametersAtMethodLevel { /* * Two test methods accepting parameters/arguments */ @Parameters({"testParameters1","testParameters2"}) @Test public void testMethod1(String testParameters1, String testParameters2) { System.out.println("Paramters one for test method 1: "+testParameters1); System.out.println("Paramters two for test method 1: "+testParameters2); } @Parameters({"testParameters3","testParameters4"}) @Test public void testMethod2(String testParameters1, String testParameters2) { System.out.println("Paramters one for test method 2: "+testParameters1); System.out.println("Paramters two for test method 2: "+testParameters2); } }
Sample Testng.xml file:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="Suite"> <test thread-count="5" name="Test"> <classes> <class name="com.softwaretestingo.testng.parameterization.PassingParametersAtMethodLevel"> <methods> <!-- Including individual method --> <include name="testMethod1"></include> <!-- Passing parameters for first method--> <parameter name="testParameters1" value="testParameters 1" /> <parameter name="testParameters2" value="testParameters 2" /> <!-- Including another method --> <include name="testMethod2"></include> <!-- Passing parameters for second method--> <parameter name="testParameters3" value="testParameters 3" /> <parameter name="testParameters4" value="testParameters 4" /> </methods> </class> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
Output:
Paramters one for test method 1: testParameters 1 Paramters two for test method 1: testParameters 2 Paramters one for test method 2: testParameters 3 Paramters two for test method 2: testParameters 4 =============================================== Suite Total tests run: 2, Passes: 2, Failures: 0, Skips: 0 ===============================================
How to Define a Parameter as Optional?
When we are declaring a parametrized method in the testng class, then you have to pass the value from the textng.xml file; if you are not passing any value to the parameterized method from the testng.xml file and when you run the testng suite, you will get an exception.
package com.softwaretestingo.testng.parameterization; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class ParameterTest { @Test @Parameters({"testParameters1","testParameters2"}) public void testMethod(String testParameters1, String testParameters2) { System.out.println("Paramters one for test method: "+testParameters1); System.out.println("Paramters two for test method: "+testParameters2); } }
TestNG.XML
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="Suite"> <test thread-count="5" name="Test"> <classes> <class name="com.softwaretestingo.testng.parameterization.ParameterTest"></class> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
Output:
=============================================== Suite Total tests run: 1, Passes: 0, Failures: 1, Skips: 0 ===============================================
Then, the questions arise: How can you handle this situation in a test class? In TestNG, there is an option available that is optional. If we declare optional to a parameter, then when the testng class is executed that time, it will check the xml file to verify whether the parameter value is provided.
If the value is provided, then it will use that parameter value, and if the value is not provided, then that time, it will assign the default value to that parameter so that it will not throw any exception.
package Parameters; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class ParameterTest { @Test @Parameters({"testParameters1","testParameters2"}) // I used Optional annotation with parameter declaration public void testMethod(@Optional String testParameters1, @Optional String testParameters2) { System.out.println("Paramters one for test method: "+testParameters1); System.out.println("Paramters two for test method: "+testParameters2); } }
XML File:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="Suite"> <test thread-count="5" name="Test"> <classes> <class name="com.softwaretestingo.testng.parameterization.ParameterAsOptional"></class> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
In the XMl file, we have not passed any parameter value, but we have mentioned @optional in the testng class, so in this case, we are not getting any exceptions, and the default value will be printed in the console, which is null.
Paramters one for test method: null Paramters two for test method: null =============================================== Suite Total tests run: 1, Passes: 1, Failures: 0, Skips: 0 ===============================================
This time, let us pass some value and check how this works:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Suite"> <parameter name="testParameters1" value="testParameters 1" /> <test thread-count="5" name="Test1"> <classes> <class name="Parameters.ParameterTest"></class> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
As we have passed one value and that value is printed, it prints as null for the other value.
Paramters one for test method: testParameters 1 Paramters two for test method: null =============================================== Suite Total tests run: 1, Passes: 1, Failures: 0, Skips: 0 ===============================================
What Will Happen If We Use Primitive Data Types In TestNg Class?
In the above example, we have taken the data type as String, But let us take an exciting point and check what will happen when we try to use the primitive data types as the parameter in the testNG class.
package com.softwaretestingo.testng.parameterization; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class PassingPrimitiveDataTypeAsParameter { @Test @Parameters({"testParameters1","testParameters2"}) // I used Optional annotation with parameter declaration public void testMethod(@Optional String testParameters1, @Optional int testParameters2) { System.out.println("Paramters one for test method: "+testParameters1); System.out.println("Paramters two for test method: "+testParameters2); } }
Xml File:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="Suite"> <test thread-count="5" name="Test"> <classes> <class name="com.softwaretestingo.testng.parameterization.PassingPrimitiveDataTypeAsParameter"></class> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
In this example, we have not passed any value for “testParameters2” from the xml file. Still, as we mentioned earlier, for the string parameter, it assigns the default value null, but for the primitive data type Int, it mentions unmatched arguments, and we are also getting MethodMatcherException.
Note: Do not use primitive data type. Use wrapper of primitive. E.g., Integer for int.
Updated TestNG Class:
package com.softwaretestingo.testng.parameterization; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class PassingWrapperClassDataTypeAsParameter { @Test @Parameters({"testParameters1","testParameters2"}) // I used Optional annotation with parameter declaration public void testMethod(@Optional String testParameters1, @Optional Integer testParameters2) { System.out.println("Paramters one for test method: "+testParameters1); System.out.println("Paramters two for test method: "+testParameters2); } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="Suite"> <test thread-count="5" name="Test"> <classes> <class name="com.softwaretestingo.testng.parameterization.PassingWrapperClassDataTypeAsParameter"></class> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
In the above XMl file, we are not passing any parameter value, but in our TestNg class, we are using Warper classes. That’s why the default value is assigned to them, which will be printed in the console.
How do you override the default value of Optional parameters?
In the above example, we have seen that the default value is assigned to the parameter when we use the @optional annotation and don’t pass any value from the testng.xml file. But if we don’t want to use the default value, then we can override the default value.
To do that, we need to pass another parameter name inside the @optional annotation as an attribute So that if the actual value for the parameter is not given, then the mentioned value will be used.
If, for testParameters1, we have not passed any value from testng xml, that time TestNG will see if any value is passed in “optionalParames.” If it is given, that value will be used. If none of the parameter values is passed, the default value will be assigned by TestNG.
package Parameters; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class ParameterTest { @Test @Parameters({"testParameters1","testParameters2"}) /* * If "testParameters1" not found in testng xml use value provided for parameter "optionalParames". * If none is present in testng xml, use null. */ public void testMethod(@Optional("optionalParames") String testParameters1, @Optional("optionalParames") String testParameters2) { System.out.println("Paramters one for test method: "+testParameters1); System.out.println("Paramters two for test method: "+testParameters2); } }
TestNg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="Suite"> <parameter name="optionalParames" value="OptionalValue" /> <test thread-count="5" name="Test"> <classes> <class name="com.softwaretestingo.testng.parameterization.PassingValuesUsingOptionalParames"></class> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
How do you use different data types in TestNG methods?
While writing the automation script using the testng automation framework, we saw most places where we used only string data types. But we can also use various data types like string, boolean, and integer.
Let us understand with a simple program:
package com.softwaretestingo.testng.parameterization; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class ParametersWithDifferentDatatypes { /* * TestNG will convert automatically passed parameter value sin desired data * types. */ @Parameters({ "StringParam", "intParam", "booleanParam" }) @Test /* * A test method accepting argument of different datatypes */ public void sampleTest(String stringParaValue, int intParamValue, boolean booleanParamValue) { System.out.println("String parameter: " + stringParaValue); System.out.println("Int parameter: " + intParamValue); System.out.println("Boolean parameter: " + booleanParamValue); } }
XML File:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="Suite"> <parameter name="StringParam" value="SoftwareTestingo" /> <parameter name="intParam" value="28" /> <parameter name="booleanParam" value="true" /> <test thread-count="5" name="Test"> <classes> <class name="com.softwaretestingo.testng.parameterization.ParametersWithDifferentDatatypes"></class> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
Output:
String parameter: SoftwareTestingo Int parameter: 28 Boolean parameter: true =============================================== Suite Total tests run: 1, Passes: 1, Failures: 0, Skips: 0 ===============================================
Note:
- You must pass the correct data type value mentioned in the testng class. Otherwise, you will get NumberFormatException.
- When you use a boolean value in the testng class, if you pass true or false from the testng.xml file, the respective value will be assigned in the testng. If you have passed anything else apart from true or false, then testng will be taken that value as false.
- You Can not pass user-defined data types in the parameter. If you pass, you will get TestNGException with an Unsupported type parameter message.