Java Unit Testing

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:
Java
  <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>5.7.2</version>
      <scope>test</scope>
  </dependency>

Basic JUnit Test Case:

Java
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:
Java
  mvn test

Advanced JUnit Features

Parameterized Tests:
Allow running the same test with different parameters.

Java
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:

Java
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.

Java
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:
Java
  <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>7.4.0</version>
      <scope>test</scope>
  </dependency>

Basic TestNG Test Case:

Java
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:
Java
  mvn test

Advanced TestNG Features

Parameterized Tests:
Allow running the same test with different parameters.

Java
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:

Java
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.

Java
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.

Java
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):

Java
<?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.

Java
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.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top