Unit Testing with JUnit and TestNG
Testing is a crucial part of software development, ensuring that code works as expected and remains robust over time. In Java, JUnit and TestNG are two popular frameworks for unit testing. This section will cover the basics and advanced features of both frameworks.
Unit Testing with JUnit
JUnit is a widely used testing framework for Java that allows developers to write and run repeatable tests. It is simple to use and integrates well with many development environments and build tools.
Setting Up JUnit
Add JUnit Dependency:
- Maven:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
Basic JUnit Test Case:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class CalculatorTest {
@Test
public void testAddition() {
Calculator calculator = new Calculator();
assertEquals(5, calculator.add(2, 3));
}
}
Running Tests:
- Use your IDE’s built-in test runner, or run tests from the command line using Maven:
mvn test
Advanced JUnit Features
Parameterized Tests:
Allow running the same test with different parameters.
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.*;
public class ParameterizedTestExample {
@ParameterizedTest
@CsvSource({
"1, 1, 2",
"2, 3, 5",
"3, 5, 8"
})
public void testAddition(int a, int b, int expected) {
Calculator calculator = new Calculator();
assertEquals(expected, calculator.add(a, b));
}
}
Lifecycle Methods:
@BeforeEach
and@AfterEach
for setup and teardown methods before and after each test.@BeforeAll
and@AfterAll
for setup and teardown methods before and after all tests in the class.
Example:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
public class LifecycleTest {
@BeforeAll
public static void setupAll() {
System.out.println("Before all tests");
}
@AfterAll
public static void teardownAll() {
System.out.println("After all tests");
}
@BeforeEach
public void setup() {
System.out.println("Before each test");
}
@AfterEach
public void teardown() {
System.out.println("After each test");
}
@Test
public void testExample() {
System.out.println("Test example");
}
}
Assertions:
JUnit provides a variety of assertions to verify the expected outcome.
import static org.junit.jupiter.api.Assertions.*;
public class AssertionTest {
@Test
public void testAssertions() {
assertTrue(true);
assertFalse(false);
assertEquals(5, 5);
assertNotEquals(5, 4);
assertNull(null);
assertNotNull("not null");
assertArrayEquals(new int[]{1, 2, 3}, new int[]{1, 2, 3});
}
}
Unit Testing with TestNG
TestNG is another popular testing framework that is inspired by JUnit but introduces additional functionality, such as annotations, grouping, and parallel execution.
Setting Up TestNG
Add TestNG Dependency:
- Maven:
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version>
<scope>test</scope>
</dependency>
Basic TestNG Test Case:
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
public class CalculatorTest {
@Test
public void testAddition() {
Calculator calculator = new Calculator();
assertEquals(5, calculator.add(2, 3));
}
}
Running Tests:
- Use your IDE’s built-in test runner, or run tests from the command line using Maven:
mvn test
Advanced TestNG Features
Parameterized Tests:
Allow running the same test with different parameters.
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
public class ParameterizedTestExample {
@DataProvider(name = "additionData")
public Object[][] dataProvider() {
return new Object[][] {
{1, 1, 2},
{2, 3, 5},
{3, 5, 8}
};
}
@Test(dataProvider = "additionData")
public void testAddition(int a, int b, int expected) {
Calculator calculator = new Calculator();
assertEquals(expected, calculator.add(a, b));
}
}
Lifecycle Methods:
@BeforeMethod
and@AfterMethod
for setup and teardown methods before and after each test.@BeforeClass
and@AfterClass
for setup and teardown methods before and after all tests in the class.
Example:
import org.testng.annotations.BeforeClass;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
public class LifecycleTest {
@BeforeClass
public void setupClass() {
System.out.println("Before class");
}
@AfterClass
public void teardownClass() {
System.out.println("After class");
}
@BeforeMethod
public void setupMethod() {
System.out.println("Before each method");
}
@AfterMethod
public void teardownMethod() {
System.out.println("After each method");
}
@Test
public void testExample() {
System.out.println("Test example");
}
}
Assertions:
TestNG provides a variety of assertions to verify the expected outcome.
import static org.testng.Assert.*;
public class AssertionTest {
@Test
public void testAssertions() {
assertTrue(true);
assertFalse(false);
assertEquals(5, 5);
assertNotEquals(5, 4);
assertNull(null);
assertNotNull("not null");
assertArrayEquals(new int[]{1, 2, 3}, new int[]{1, 2, 3});
}
}
Test Grouping:
TestNG allows you to group tests and run specific groups.
import org.testng.annotations.Test;
public class GroupingTest {
@Test(groups = {"group1"})
public void testGroup1() {
System.out.println("Test in group 1");
}
@Test(groups = {"group2"})
public void testGroup2() {
System.out.println("Test in group 2");
}
}
Parallel Execution:
TestNG supports parallel execution of tests.
Example (testng.xml
):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="ParallelSuite" parallel="methods" thread-count="4">
<test name="Test1">
<classes>
<class name="com.example.ParallelTest"/>
</classes>
</test>
</suite>
Dependent Methods:
TestNG allows you to define dependencies between test methods.
import org.testng.annotations.Test;
public class DependencyTest {
@Test
public void setup() {
System.out.println("Setup method");
}
@Test(dependsOnMethods = {"setup"})
public void testMethod() {
System.out.println("Test method");
}
}
Summary
JUnit and TestNG are powerful frameworks for unit testing in Java. They provide extensive features for writing and managing tests, ensuring code quality and robustness.
- JUnit:
- Simple and widely used.
- Supports parameterized tests, lifecycle methods, and a variety of assertions.
- TestNG:
- Inspired by JUnit but adds more features.
- Supports parameterized tests, lifecycle methods, test grouping, parallel execution, and dependent methods.
By using these frameworks and leveraging their advanced features, developers can create comprehensive and effective test suites, leading to more reliable and maintainable Java applications.