Testing is integral to the Software Development Life Cycle. As technology evolves toward the digital era, testing becomes more of a necessity than a requirement. Testing helps eliminate gaps, errors, or missing requirements in the present system compared to the expected behavior. And one such type of testing is unit testing.
Unit testing refers to testing small units or chunks of code individually and independently. It is the first step during functional testing. It is performed to detect defects early by the developers. It helps in saving costs by detecting bugs early in the lifecycle. There are two types of Unit Testing – Manual testing and Automated Testing.
This article helps you understand more about the two most used Unit testing frameworks – JUnit and TestNG.
Table of ContentsJUnit is an open-source unit testing framework for Java. As the name suggests, it is used to test small code units. It is a part of the xUnit architecture. This framework is used by default by Java developers to write as well as execute test cases.
JUnit follows the approach of “testing first then coding”. This emphasizes setting up test data and testing the small piece of code first and then implementing the same. This increases productivity and helps in improving code stability. JUnit 5 is the latest version of JUnit, with the current release being 5.7.1.
Features of JUnit
1. Installing and Setting Up
<dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.7.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-runner</artifactId> <version>5.7.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.7.1</version> <scope>test</scope> </dependency> </dependencies>
2. Test Suites
The test suite consists of a collection of test cases that lets you run the test cases simultaneously. It’s like a logical grouping of test cases.
JUnit 5 uses @RunWith and @Suite Classes for creating Test Suites.
@RunWith(Suite.class) @Suite.SuiteClasses({ JUnitTestSuiteDemo1.class, JUnitTestSuitDemo2.class })
3. Test Annotations
JUnit 5 is an annotation-based framework. Annotations are tags that provide additional information about the test methods or the class. They are usually represented by ‘@’.
Some popular annotations of JUnit 5 include:
4. Exceptions Handling
Exception handling is done via Assertions.assertThrows() API. assertThrows() method asserts that the code throws an exception of the given type when executed. This method fails if there is no exception or exception of some other type.
@Test void testExpectedException() { Assertions.assertThrows(NumberFormatException.class, () -> { Integer.parseInt("Hello World"); //would fail since input is not a valid number }); }
5. Ignoring Tests
Ignoring tests is important in an automation framework since some test cases can become redundant due to changed requirements or may have to be avoided for some reason. This can be done using @Ignore in JUnit 5.
@Ignore public void oldTest(){ System.out.println("This test to be ignored"); } @Test public void newTest(){ System.out.println("Test has been executed"); }
6. Grouping Tests
Grouping tests help in identifying the tests and executing them quickly. JUnit 5 uses @Tag to group the tests.
@Test @Tag("Smoke") public void Test_a(){ System.out.println("Testa has been executed"); } @Test @Tag("Smoke") public void Test_b(){ System.out.println("Testb has been executed"); }
7. Parameterizing Tests
Parameterizing Tests helps test the same test scenario with different sets of inputs. In JUnit 5, the following dependency needs to be added to parameterize the tests along with @ParameterizedTest.
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>5.7.1</version> <scope>test</scope> </dependency> @ParameterizedTest @ValueSource(ints = {1, 3, 5, -3, 15, Integer.MAX_VALUE}) // six numbers void isOdd_ShouldReturnTrueForOdd(int num) { assertTrue(Numbers.isOdd(num)); }
A simple JUnit test case will look something like this.
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import com.howtodoinjava.junit5.examples.Calculator; public class DemoApp { @BeforeAll static void setupFirst(){ System.out.println("@BeforeAll executed"); } @BeforeEach void setupSecond(){ System.out.println("@BeforeEach executed"); } @Tag("DEV") @Test void testCalculatorOne() { System.out.println("======TEST ONE ADD EXECUTED======="); Assertions.assertEquals( 8 , Calculator.add(4, 4)); } @Tag("PROD") @Disabled @Test void testCalculatorTwo() { System.out.println("======TEST TWO ADD EXECUTED======="); Assertions.assertEquals( 10 , Calculator.add(6, 4)); } @AfterEach void tearThis(){ System.out.println("@AfterEach executed"); } @AfterAll static void tear(){ System.out.println("@AfterAll executed"); } }
TestNG is a Java-based test automation framework that was inspired by JUnit. It overcomes all the limitations of JUnit along with additional functionalities. This makes it more powerful and easier to use. NG here stands for “Next Generation”.
It is designed to cover a range of test categories such as Unit testing, Functional testing, Integration testing, etc. It is extremely popular and helps testers organize the test cases in a structured way. This helps in maintaining the readability of the scripts. The current version of TestNG is 7.6.0.
Features of TestNG
1. Installing and setting up
<dependency> <groupId>org.TestNG</groupId> <artifactId>TestNG</artifactId> <version>7.6.0</version> <scope>test</scope> </dependency>
2. Test Suites
In TestNG, Test Suites are defined in an XML file.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://TestNG.org/TestNG-1.0.dtd"> <suite name="TestSuite" parallel="tests"> <test name="DemoTest"> <classes> <class name="com.pages.LoginPageTest"></class> <class name="com.pages.HomePageTest"></class> <class name="com.pages.ProductPageTest"></class> </classes> </test> </suite>
3. Test Annotations
Some popular annotations of TestNG include:
4. Exceptions Handling
Exception handling is done via the expectedExceptions parameter along with @Test annotation.
@Test(expectedExceptions = ArithmeticException.class) public void DivideByZeroTest() { int i = 20/0; }
5. Ignoring Tests
Ignoring tests can be done by passing a parameter in the @Test method.
@Test public void test1() { System.out.println("This is test1"); } @Test(enabled = false) public void test2() { System.out.println("This is test2"); }
6.Grouping Tests
Grouping tests can be done by passing parameters in the @Test method.
@Test(groups = { "Sanity", "Regression" }) public void test_method1() { //Test implementation }
7. Parameterizing Tests
Parameterizing tests are done by @Parameters. The parameter value is passed in the TestNG XML file.
@Test() @Parameters("username") public void test1(String username) { System.out.println("The username "+username + " is passed"); } <test name="DemoTest"> <parameter name = "username" value = "Alex"/>
Parameterizing tests can also be done by @DataProviders. It enables multiple input values to test.
@Test(dataProvider = "credentials")
A simple TestNG test case will look something like this.
package newtest.com; import org.testng.annotations.Test; import org.testng.annotations.BeforeMethod; import org.testng.annotations.AfterMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.BeforeClass; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeTest; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeSuite; import org.testng.annotations.AfterSuite; public class NewTestng { @Test(dataProvider = "dp") public void f(Integer n, String s) { System.out.println(" *Parameterized method*"); System.out.println("Integer "+n+" String "+s); } @BeforeMethod public void beforeMethod() { System.out.println("Before Method"); } @AfterMethod public void afterMethod() { System.out.println("After Method"); } @DataProvider public Object[][] dp() { return new Object[][] { new Object[] { 1, "a" }, new Object[] { 2, "b"}, }; } @BeforeClass public void beforeClass() { System.out.println("Before Class"); } @AfterClass public void afterClass() { System.out.println("After Class"); } @BeforeTest public void beforeTest() { System.out.println("Before Test"); } @AfterTest public void afterTest() { System.out.println("After Test"); } @BeforeSuite public void beforeSuite() { System.out.println("Before Suite"); } @AfterSuite public void afterSuite() { System.out.println("After Suite"); } }
Here is the tabular comparison between JUnit and TestNG:
Criteria | JUnit | TestNG |
---|---|---|
Framework | Open Source Testing framework | Open Source Java-based Testing framework |
Supported Testing | Unit Testing | Unit Testing, Functional Testing, Integration Testing, end-to-end Testing, etc. |
Annotations | Do not support advanced annotation like @BeforeGroups, @AfterGroups | Supports advanced and unique annotations like @BeforeGroups, @AfterGroups |
Test Suite | Uses @RunWith, @Suite to run the test suite | Uses an XML file to run the test suite |
Dependency Tests | Missing Dependency tests | Supports Dependency Tests |
Grouping Tests | Does not provide Grouping of test cases together | Allows Grouping and executing of test cases together |
Order of Tests | Does not support | Supports ordering of test methods via a priority attribute |
Assumptions | Supports Assumptions to skip tests based on certain conditions | Does not support Assumptions |
Custom Name | Provides provision for Custom descriptive names for tests | Does not provide Custom names |
Reporting | Integrates with Maven to generate HTML reports | Has built-in HTML reports |
Listeners | Supports listeners through Listeners API | Supports listeners through annotations |
Ease of use | Running tests requires a certain amount of dependency Eg. for parameterization, one might need JUnit Jupiter | Writing and running tests is very easy |
JUnit and TestNG are popular test automation frameworks in the Java community and used widely by people. TestNG however, only provides a few additional features in comparison to Junit. Hence, the choice of the “right” testing framework for test automation is purely based on the project requirements and flexibility.
Irrespective of whether you choose TestNG or JUnit, the true potential of these frameworks can only be exploited by running the tests on real browsers and devices under real user conditions. BrowserStack offers a Cloud-based Selenium grid of 3000+ real browsers and devices for testing purposes. BrowserStack allows you to perform manual and automated tests using different frameworks and languages. You can seamlessly run Selenium, Cypress, Playwright, and Puppeteer tests on 3000+ devices and browsers.
ncG1vNJzZmivp6x7o77OsKqeqqOprqS3jZympmeXqralsY6jrKehpGLDtHnTnqqtppc%3D