In this article, I will be showing you an example of TestNG factory.
We use TestNG @Factory
to create multiple test instances. The test class may either have the default no-arg constructor or constructor with one or more arguments.
TestNG factory mechanism
In order to understand how a TestNG factory works, we first we need to know how it executes a test method.
In the below class, we run TestNG programmatically.
TestNGRunExample:
package testNGFactory; import org.testng.TestNG; import com.javarticles.testng.UITesting; public class TestNGRunExample { public static void main(String[] args) { TestNG testng = new TestNG(); testng.setTestClasses(new Class[] { UITesting.class }); testng.run(); } }
Output:
[TestNG] Running: Command line suite someTest: web someTest: swing someTest: JSF =============================================== Command line suite Total tests run: 3, Failures: 0, Skips: 0 ===============================================
When testing.run()
is called, internally, TestNG framework instantiates the classes provided by invoking their no-argument constructor and then proceeds to run all the test methods that can be found on each class.
But what happens if the class doesn’t have a no-argument constructor. In the example below, DummyTestClass
has a constructor which takes a param
and it doesn’t have a default constructor.
TestNGRunConstructorWithParamExample:
package testNGFactory; import org.testng.TestNG; import com.javarticles.testng.DummyTestClass; public class TestNGRunConstructorWithParamExample { public static void main(String[] args) { TestNG testng = new TestNG(); testng.setTestClasses(new Class[] { DummyTestClass.class }); testng.run(); } }
package com.javarticles.testng; import org.testng.annotations.Test; public class DummyTestClass { private String param; public DummyTestClass(String param) { this.param = param; } @Test public void dummyTest() { System.out.println("Param is " + param); } }
Output:
[TestNG] Running: C:\javarticles_ws\testNGFactory\testng.xml Param is Default test name =============================================== Suite Total tests run: 1, Failures: 0, Skips: 0 ===============================================
The test case ran fine but since we didn’t provide any argument value, TestNG itself has injected a default value “Default test name”.
Since the class expects a parameter value, we should have a way to create test instances for different values. In order to address this scenario, TestNG provides you with @Factory
feature where you can instantiate the test classes yourself. This is done with the @Factory
annotation, which must be put on top of a method that returns an array of objects. Each of these objects should be an instance of a class that contains TestNG annotations. The return type of a @Factory
method is always Object[]
.
These objects will also be inspected by TestNG to find out whether they have @Factory
annotations as well, in which case the cycle starts again, until TestNG is left with either instances that have no @Factory
annotations on them or instances that have @Factory
methods that have already been invoked.
TestNG @Factory annotation Example
In this example, test class MultipleFactoryTest
has a constructor with parameter param
. It contains a @Test
method t
. It also has a @Factory
method called create
.
In create()
, we return an instance of MultipleFactoryTest
and TestClass1
. TestNG will run the test lifecycle on the returned test instances. The test class TestClass1
itself has a @Factory
annotated method create()
which returns an instance of TestClass2
. Likewise, TestClass2
creates an instance of TestClass3
.
When we run MultipleFactoryTest
, it ends up running tests of MultipleFactoryTest
, TestClass1
, TestClass2
and TestClass3
.
Though TestNG can create many instances using @Factory
, it guarantees that your @Factory
method will be invoked exactly once.
MultipleFactoryTest:
package com.javarticles.testng; import org.testng.annotations.Factory; import org.testng.annotations.Test; public class MultipleFactoryTest { private String param; public MultipleFactoryTest(String param) { this.param = param; } @Test public void t() { System.out.println("MultipleFactoryTest:t:" + param); } @Factory public Object[] create() { return new Object[] { new MultipleFactoryTest("Multiple factory test"), new TestClass1()}; } }
TestClass1:
package com.javarticles.testng; import org.testng.annotations.Factory; import org.testng.annotations.Test; public class TestClass1 { @Test public void t() { System.out.println("TestClass1.t"); } @Factory public Object[] create() { return new Object[] { new TestClass2()}; } }
TestClass2:
package com.javarticles.testng; import org.testng.annotations.Factory; import org.testng.annotations.Test; public class TestClass2 { @Test public void t() { System.out.println("TestClass2.t"); } @Factory public Object[] create() { return new Object[] { new TestClass3() }; } }
TestClass3:
package com.javarticles.testng; import org.testng.annotations.Test; public class TestClass3 { @Test public void t() { System.out.println("TestClass3.t"); } }
Output:
[TestNG] Running: C:\Users\mokkara\AppData\Local\Temp\testng-eclipse-564478107\testng-customsuite.xml MultipleFactoryTest:t:Multiple factory test TestClass1.t TestClass2.t TestClass3.t PASSED: t PASSED: t PASSED: t PASSED: t =============================================== Default test Tests run: 4, Failures: 0, Skips: 0 =============================================== =============================================== Default suite Total tests run: 4, Failures: 0, Skips: 0
Download Eclipse Project
Download the source code here: testNGFactory.zip