TestNG Vs JUnit: Test Framework Comparison

JUnit 4 and TestNG are the popular testing frameworks in application development using Java. Both frameworks look very similar in functionality. 

TestNG is a testing framework inspired from JUnit and NUnit but introducing some new functionalities that make it more powerful and easier to use (https://testng.org)

JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks. (https://junit.org)

Test Setup

Depends on which version of the JUnit you are using, JUnit offers initialization and cleanup at two levels, before and after each method and class. 

JUnit 4: 
@Before - executed before each test.
@After - execute after each test.

@BeforeClass - executed once before class.
@AfterClass - executed one after class.
JUnit 5: 
@BeforeEach - executed before each test.
@AfterEach - execute after each test.

@BeforeAll - executed once before class.
@AfterAll - executed one after class.

Order of Execution.
Dashed box -> optional annotation.

enter image description here

Sample Test Class:

public class SampleTest {

    private SampleClass sampleClass;

    @BeforeClass
    public static void beforeClassFunction(){
        System.out.println("Before Class");
    }

    @Before
    public void beforeFunction(){
        sampleClass=new SampleClass();
        System.out.println("Before Function");
    }

    @After
    public void afterFunction(){
        System.out.println("After Function");
    }

    @AfterClass
    public static void afterClassFunction(){
        System.out.println("After Class");
    }

    @Test
    public void initializeTest(){
        Assert.assertEquals("Initailization check", "Initialize", sampleClass.initializeData() );
    }

    @Test
    public void processTest(){
        Assert.assertEquals("Process check", "Process", sampleClass.processDate() );
    }

}
Output:
Before Class
Before Function
After Function
Before Function
After Function
After Class

Similar to JUnit above, TestNG also provides initialization and cleanup at the method and class level are @BeforeClass and @AfterClass remain the same at the class level, and the method level annotations are @BeforeMethod and @AfterMethod:

Sample Test Class

public class SampleTest {

    private SampleClass sampleClass;

    @BeforeClass
    public static void beforeClassFunction(){
        System.out.println("Before Class");
    }

    @BeforeMethod
    public void beforeFunction(){
        sampleClass=new SampleClass();
        System.out.println("Before Function");
    }

    @AfterMethod
    public void afterFunction(){
        System.out.println("After Function");
    }

    @AfterClass
    public static void afterClassFunction(){
        System.out.println("After Class");
    }

    @Test
    public void initializeTest(){
        Assert.assertEquals("Initailization check", "Initialize", sampleClass.initializeData() );
    }

    @Test
    public void processTest(){
        Assert.assertEquals("Process check", "Process", sampleClass.processDate() );
    }

}

In addition to above annotations, TestNG offers more annotations

AnnotationDescription
@BeforeSuite
The annotated method will be run before all tests in this suite have run.
@BeforeTest
The annotated method will be run before any test method belonging to the classes inside the test tag is run.
@BeforeGroups
The list of groups that this configuration method will run before. This method is guaranteed to run shortly before the first test method that belongs to any of these groups is invoked.
@AfterSuite
The annotated method will be run after all tests in this suite have run.
@AfterTest
The annotated method will be run after all the test methods belonging to the classes inside the test tag have run.
@AfterGroupsThe list of groups that this configuration method will run after. This method is guaranteed to run shortly after the last test method that belongs to any of these groups is invoked.

Test Suites

Test suites help to the group and execute tests in bulk. Executing tests separately for all test classes is not desired in most cases. Test suites help in achieving this grouping. This feature is available in both JUnit and TestNG. However, both are implemented in a very different method.

In JUnit, test suites can be created and executed with these annotations.
@RunWith
@SuiteClasses

In JUnit 4

@RunWith(Suite.class)
@SuiteClasses({ RegistrationTest.class, FunctionalTest.class })
public class SuiteTest {
     //
}
In JUnit 5

@RunWith(JUnitPlatform.class)
@SelectClasses({RegistrationTest.class, FunctionalTest.class})
public class SuiteTest {
      //
}

In TestNG we can group tests by using an TestNG XML file:

<suite name="Time test Suite" verbose="1">
    <test name="Feature Testing">
        <classes>
            <class name="com.tests.RegistrationTest" />
            <class name="com.tests.FunctionalTest" />
        </classes>
    </test>
</suite>

Apart from grouping classes, TestNG can group methods as well using the @Test(groups=”groupName”)annotation:

Sample Test Class: In this class we are adding group name to methods

@Test(groups = "testgroup")
public void testGroupTest() {
    System.out.println("Test Group Test");
}

@Test(groups = "regression")
public void tegressionTest() {
    System.out.println("Regression Group test");
}

@Test(groups = "testgroup")
public void anotherTestGroupTest() {
    System.out.println("Another Test Group Test");
}

Sample TestNG.xml file . — Only “testgroup” methods will be executed.

<suite name="Time test Suite" verbose="1">
    <test name="Feature Testing">
        <groups>
            <run>
               <include name="testgroup" />
            </run>
        </groups>
        <classes>
            <class name="com.tests.RegistrationTest" />
            <class name="com.tests.FunctionalTest" />
        </classes>
    </test>
</suite>

Ignoring Tests

Ignoring some test methods execution is possible in both TestNG and JUNit.

In JUnit:

@Ignore
@Test
public void jUnitIgnoreTest() {
    System.out.println("This test won't execute");
}
In TestNG:

@Test(enabled=false)
public void testNGIgnoreTest() {
    System.out.println("This test won't execute");
}

Dependent On

Dependency is a feature in TestNG that allows a test method to depend on a single or a group of test methods. This will help in executing a set of tests to be executed before a test method. Method dependency only works if the “depend-on-method” is part of the same class or any of the inherited base class.

Below class having a test with multiple test methods dependencies — firstTest method is depending on secondTest and thirdTest (which means if both the tests are successful then first method will execute)

public class DependentTestDemo
{
    @Test(dependsOnMethods = { "secondTest", "thirdTest" })
    public void firstTest() {
        System.out.println("First Test Method");
    }
 
    @Test
    public void secondTest() {
        System.out.println("Second Test Method");
    }
 
    @Test
    public void thirdTest() {
        System.out.println("Third Test Method");
    }
}
Output:
Second Test Method
Third Test Method
First Test Method

Data Parameters – Passing Parameters to Tests

This feature allows the user to pass parameters to tests as arguments. Parameterized unit tests are helpful for testing the same code under several conditions. The main idea is to make the unit test method more reusable and to test with a different set of inputs.

In JUnit 5, we have the advantage of test methods consuming data arguments directly from the configured source. By default, JUnit 5 provides a few source annotations like:

@ValueSource: we can use this with an array of values of type Short, Byte, Int, Long, Float, Double, Char, and String:

@ParameterizedTest
@ValueSource(strings = { "Hi", "Bye" })
public void saySomethingTest(String word) {
    assertNotNull(word);
}

@EnumSource – passes Enum constants as parameters to the test method:

@ParameterizedTest
@EnumSource(value = SomeFunctionality.class, names = {"PING", "PONG"})
public void pingPongTest(SomeFunctionality timeUnit) {
    assertTrue(EnumSet.of(SomeFunctionality.PING, SomeFunctionality.PONG).contains(timeUnit));
}

@MethodSource – passes external methods generating streams:

static Stream<String> testDataProvider() {
    return Stream.of("ping", "pong");
}
 
@ParameterizedTest
@MethodSource("testDataProvider")
void testingDataProviderTest(String str) {
    System.out.println(str);
}

@CsvSource – uses CSV values as a source for the parameters:

@ParameterizedTest
@CsvSource({ "111, Dave", "222, John", "333, Mike" })
void csvDataProviderTest(int empId, String empName) {
    System.out.println(empId);
    System.out.println(empName);
    assertTrue(1);
}

There is another feature called RepeatedTest Annotation in JUnit5

@Test
@DisplayName("Repeating Test")
@RepeatedTest(5)
void repeatDisplayTextTest(TestInfo testInfo) {
    System.out.println("Repeating Test using Test Annotation");
}
Output:
Repeating Test using Test Annotation
Repeating Test using Test Annotation
Repeating Test using Test Annotation
Repeating Test using Test Annotation
Repeating Test using Test Annotation

In TestNG, we can parametrize tests using @Parameter or @DataProvider annotations. While using the XML file annotate the test method with @Parameter:

@Test
@Parameters({"number", "squareValue"})
public void squareValueTest(int number, int squareValue) {
    Assert.assertEquals(squareValue, (number * number));
}

and provide the data in the XML file:

<suite name="TestSuite">
    <test name="TestSquareValue">
        <parameter name="number" value="6"/>
        <parameter name="squareValue" value="36"/>
        <classes>
            <class name="com.test.SimpleMathTests"/>
        </classes>
    </test>
</suite>

TestNG DataProvider – Data Driven Testing

An important feature provided by TestNG is the DataProvider feature for data-driven testing. It helps you to write data-driven tests which essentially means that the same test method can be run multiple times with different sets of data.

Here’s an example of using @DataProvider for primitive data types:

public class SampleMathTestClass
{
  @DataProvider(name = "data-provider")
  public static Object[][] squareValue() {
      return new Object[][]{{3, 9}, {6, 36}, {9, 80}};
  }
 
  @Test(dataProvider = "data-provider")
  public void squareValueTest (Integer number, Integer expected) {
      Assert.assertEquals(expected, number * number);
  }
}

And @DataProvider for objects: @DataProvider and @Test are in different classes.

public class TestClass
{
    @Test(dataProvider = "numbersObject", dataProviderClass = DataProviderClass.class)
    public void checkEvenNumberTest(EvenNumber number) {
        Assert.assertEquals(number.isEven(), number.getValue() % 2 == 0);
    }
}


public class DataProviderClass
{
   @DataProvider(name = "numbersObject")
   public Object[][] testDataProvider() {
       return new Object[][]{{new EvenNumber(1, false)},
         {new EvenNumber(2, true)}, {new EvenNumber(4, true)}};
   }
}

TestNG parallel execution of tests

TestNG parallel execution of tests, classes, and suites with examples. Learn how to run testng tests and suites in a parallel or single test in multiple threads.

TO run methods in parallel – create testng xml file like this.

<suite name="TestSuite" parallel="methods" thread-count="5" >
  <test name="ParallelMethodTest" group-by-instances="true">
    <classes>
      <class name="com.test.ParallelRunMethodsTest" />
    </classes>
  </test>
</suite>

TO run classes in parallel – create testng xml file like this.

<suite name="TestSuite" parallel="classes" thread-count="5" >
  <test name="PrallelClassTest" >
    <classes>
      <class name="com.test.ParallelRunClassOneTest"/>
      <class name="com.test.ParallelRunClassTwoTest"/>
      <class name="com.test.ParallelRunClassThreeTest"/>
    </classes>
  </test>
</suite>

Annotations Comparison between TestNG and JUnit

DescriptionTestNGJUnit 4
Test annotation@Test@Test
Executes before the first test method is invoked in the current class@BeforeClass@BeforeClass
Executes after all the test methods in the current class@AfterClass@AfterClass
Executes before each test method@BeforeMethod@Before
Executes after each test method@AfterMethod@After
annotation to ignore a test@Test(enable=false)@ignore
annotation for exception@Test(expectedExceptions = ArithmeticException.class)@Test(expected = ArithmeticException.class)
timeout@Test(timeout = 1000)@Test(timeout = 1000)
Executes before all tests in the suite@BeforeSuiten/a
Executes after all tests in the suite@AfterSuiten/a
Executes before a test runs@BeforeTestn/a
Executes after a test runs@AfterTestn/a
Executes before the first test method is invoked that belongs to any of these groups is invoked@BeforeGroupsn/a
run after the last test method that belongs to any of the groups here@AfterGroupsn/a
To supply the data to test methods@DataProvidern/a

Conclusion

Above features comparison of TestNG vs JUnit should help you in finding the perfect fit for your application. Both of these are good testing frameworks for java applications.

Leave a Reply

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