Multiple test configurations with TestNG

I use TestNG for automated testing as it has a number of features that JUnit doesn’t have. One of its strengths is the power and flexibility of configuring tests and supplying parameters to them, for which there are two methods:

  1. Using the testng.xml file to define both the list of tests to run and parameters.
  2. Using data providers within your test code to load parameters.

Using the testng.xml file is very simple, but not as powerful as using data providers. However, suppose you’d like to run the same set of tests with different parameters, but without having to write data providers – how could you achieve that?

One mechanism is to allow test writers to specify parameters in two files which are merged by Ant into a single testng.xml file before running the tests:

  • A testng-default.xml file with the list of tests and default parameters.
  • A .properties to supply overrides or additional parameters.

Merging files like this is a task very suited to a scripting language. Here is an implementation in groovy:

println "Merging testng-default.xml with .properties file into testng.xml"

if (args.size() != 3) {
    println "Script requires three arguments:"
    println "Path to testng template file"
    println "Path to .properties file"
    println "Path for saving merged testng file"
}

String testNGTemplate = args[0]
String propertiesFile = args[1]
String mergedTestNGFile = args[2]

// get the properties
File propsFile = new File(propertiesFile)
FileInputStream input = new FileInputStream(propsFile)
Properties props = new Properties()
props.load(input)

// Start copying the testng file
File oldTestNG = new File(testNGTemplate)
File newTestNG = new File(mergedTestNGFile)

// Get the set of parameters. Read in using Groovy XML parser.
Node xml = new XmlParser().parse(oldTestNG)
NodeList parameterList = xml.test.parameters.parameter
// iterate over parameter list and replace any that have been overridden
parameterList.each{p ->
    println p.@name
    if (props.keySet().contains(p.@name)) {
        println "Replacing ${p.@name} with value from .properties file"
        // update the value to the one set in .properties
        p.@value = props.get(p.@name)
    }
}

// Now add any properties that weren't listed in the original
// testng-default.xml file
Set<String> parameterNames = xml.test.parameters.parameter.@name
Node parameters = xml.test.parameters.getAt(0)

props.each{ key, value ->
  if (!parameterNames.contains(key)) {
    println "Adding property from .properties. Key: ${key}, value: ${value}"
    parameters.appendNode("parameter",[name: "${key}", value: "${value}"])
  }    
}

// Write out the file
new XmlNodePrinter(new PrintWriter(newTestNG)).print(xml)

Note that this script is written assuming that all of your test classes are part of a single “test”, with the parameters inside that test, like the following:

<suite name="regression-tests">
    <test name="regression-basic">
        <parameters>
            <parameter name="param1" value="value1"/>
            <parameter name="param2" value="value2"/>
            <parameter name="param3" value="value3"/>
        </parameters>
        <classes>
            <class name="uk.co.somecorp.Test1" />
            <class name="uk.co.somecorp.Test2" />      
            <class name="uk.co.somecorp.Test3" />
        </classes>
    </test>
</suite>

This is reflected in the lines in the Groovy script that navigate to the parameters, such as:

NodeList parameterList = xml.test.parameters.parameter

Obviously if you want to organise your default testng file differently you’ll need to adjust these parts of the script.

You can invoke the script from Ant with:

<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy">
   <classpath>
      <fileset file="/path_to_groovy/groovy-all.jar"/>
   </classpath>
</taskdef>    

<!-- Target to merge the testng-default.xml and .properties files -->
  <target name="merge-files">
     <groovy src="/path_to_script/MergeTestNGAndProperties.groovy">
        <arg value="/some_path/testng-default.xml"/>
        <arg value="/some_path/my.properties"/>
        <arg value="/output_path/testng.xml"/>
     </groovy>
</target>
This entry was posted in Groovy, Testing and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

503,610 Spambots Blocked by Simple Comments

HTML tags are not allowed.