Back to Chapters

Chapter 4: Assertions and Validations

Basic Assertions

Assertions are the foundation of test validation in TestNG. They allow you to verify that your code behaves as expected by comparing actual results with expected values. TestNG provides a rich set of assertion methods through the org.testng.Assert class.

assertEquals

The most commonly used assertion is assertEquals, which verifies that two values are equal:

import org.testng.Assert;
import org.testng.annotations.Test;

public class EqualsAssertionExample {

    @Test
    public void testStringEquality() {
        String actual = "TestNG";
        String expected = "TestNG";
        Assert.assertEquals(actual, expected, "Strings should be equal");
    }

    @Test
    public void testNumericEquality() {
        int actual = 5 + 3;
        int expected = 8;
        Assert.assertEquals(actual, expected, "Addition result should be 8");
    }

    @Test
    public void testDoubleEquality() {
        double actual = 5.01;
        double expected = 5.0;
        double delta = 0.1; // Acceptable difference
        Assert.assertEquals(actual, expected, delta, "Doubles should be equal within delta");
    }
}

assertNotEquals

This assertion verifies that two values are not equal:

import org.testng.Assert;
import org.testng.annotations.Test;

public class NotEqualsAssertionExample {

    @Test
    public void testStringInequality() {
        String actual = "TestNG";
        String expected = "JUnit";
        Assert.assertNotEquals(actual, expected, "Strings should not be equal");
    }

    @Test
    public void testNumericInequality() {
        int actual = 5 + 3;
        int expected = 9;
        Assert.assertNotEquals(actual, expected, "Addition result should not be 9");
    }
}

assertTrue and assertFalse

These assertions verify boolean conditions:

import org.testng.Assert;
import org.testng.annotations.Test;

public class BooleanAssertionExample {

    @Test
    public void testTrueCondition() {
        boolean condition = 5 > 3;
        Assert.assertTrue(condition, "5 should be greater than 3");
    }

    @Test
    public void testFalseCondition() {
        boolean condition = 5 < 3;
        Assert.assertFalse(condition, "5 should not be less than 3");
    }

    @Test
    public void testStringContains() {
        String text = "TestNG is a testing framework";
        Assert.assertTrue(text.contains("TestNG"), "Text should contain 'TestNG'");
        Assert.assertFalse(text.contains("JUnit"), "Text should not contain 'JUnit'");
    }
}

assertNull and assertNotNull

These assertions verify that an object is null or not null:

import org.testng.Assert;
import org.testng.annotations.Test;

public class NullAssertionExample {

    @Test
    public void testNullObject() {
        Object obj = null;
        Assert.assertNull(obj, "Object should be null");
    }

    @Test
    public void testNotNullObject() {
        Object obj = new Object();
        Assert.assertNotNull(obj, "Object should not be null");
    }

    @Test
    public void testMethodReturningNull() {
        String result = getValueFromDatabase("nonexistent");
        Assert.assertNull(result, "Result should be null for nonexistent key");
    }

    private String getValueFromDatabase(String key) {
        // Simulate database lookup
        if ("nonexistent".equals(key)) {
            return null;
        }
        return "some value";
    }
}

assertSame and assertNotSame

These assertions verify object identity (reference equality) rather than value equality:

import org.testng.Assert;
import org.testng.annotations.Test;

public class SameAssertionExample {

    @Test
    public void testSameObject() {
        Object obj1 = new Object();
        Object obj2 = obj1; // Same reference
        Assert.assertSame(obj1, obj2, "Objects should be the same instance");
    }

    @Test
    public void testNotSameObject() {
        Object obj1 = new Object();
        Object obj2 = new Object(); // Different reference
        Assert.assertNotSame(obj1, obj2, "Objects should be different instances");
    }

    @Test
    public void testStringIntern() {
        String s1 = "TestNG"; // String literal, goes to string pool
        String s2 = new String("TestNG").intern(); // Explicitly interned
        Assert.assertSame(s1, s2, "Interned strings should be the same instance");
    }
}

Advanced Assertions

TestNG provides more advanced assertion capabilities for complex validation scenarios.

assertEqualsNoOrder

This assertion verifies that two arrays contain the same elements, regardless of their order:

import org.testng.Assert;
import org.testng.annotations.Test;

public class ArrayAssertionExample {

    @Test
    public void testArrayEquality() {
        String[] actual = {"TestNG", "JUnit", "Mockito"};
        String[] expected = {"JUnit", "TestNG", "Mockito"};

        // This would fail with assertEquals
        // Assert.assertEquals(actual, expected, "Arrays should be equal");

        // This passes because order doesn't matter
        Assert.assertEqualsNoOrder(actual, expected, "Arrays should contain the same elements");
    }
}

assertThrows

This assertion verifies that a specific exception is thrown:

import org.testng.Assert;
import org.testng.annotations.Test;

public class ExceptionAssertionExample {

    @Test
    public void testExceptionThrown() {
        Assert.assertThrows(ArithmeticException.class, () -> {
            int result = 1 / 0; // This should throw ArithmeticException
        });
    }

    @Test
    public void testExceptionWithMessage() {
        ArithmeticException exception = Assert.expectThrows(ArithmeticException.class, () -> {
            int result = 1 / 0;
        });

        Assert.assertEquals(exception.getMessage(), "/ by zero", "Exception message should match");
    }
}

Collection Assertions

TestNG doesn't have built-in collection assertions, but you can use Java's collection methods with standard assertions:

import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.Arrays;
import java.util.List;

public class CollectionAssertionExample {

    @Test
    public void testListContents() {
        List<String> actual = Arrays.asList("TestNG", "JUnit", "Mockito");

        // Check size
        Assert.assertEquals(actual.size(), 3, "List should contain 3 elements");

        // Check specific element
        Assert.assertEquals(actual.get(0), "TestNG", "First element should be TestNG");

        // Check if list contains element
        Assert.assertTrue(actual.contains("Mockito"), "List should contain Mockito");

        // Check if list contains all elements
        List<String> expected = Arrays.asList("TestNG", "Mockito");
        Assert.assertTrue(actual.containsAll(expected), "List should contain all expected elements");
    }
}

Soft Assertions

Standard assertions in TestNG stop test execution when an assertion fails. Soft assertions allow multiple assertions to be collected before reporting failures, which is useful when you want to check multiple conditions in a single test.

Using SoftAssert

import org.testng.annotations.Test;
import org.testng.asserts.SoftAssert;

public class SoftAssertExample {

    @Test
    public void testMultipleConditions() {
        SoftAssert softAssert = new SoftAssert();

        // These assertions will not stop execution if they fail
        softAssert.assertEquals(5 + 3, 8, "Addition should work");
        softAssert.assertEquals(5 - 3, 2, "Subtraction should work");
        softAssert.assertEquals(5 * 3, 15, "Multiplication should work");
        softAssert.assertEquals(6 / 3, 2, "Division should work");

        // This assertion will fail but execution continues
        softAssert.assertEquals(5 / 2, 3, "Integer division should work");

        // More assertions
        softAssert.assertTrue(5 > 3, "5 should be greater than 3");

        // This is required to throw an exception if any assertions failed
        softAssert.assertAll();
    }
}

Custom Soft Assert Implementation

You can create a custom soft assertion class to add domain-specific assertions:

import org.testng.asserts.SoftAssert;

public class CustomSoftAssert extends SoftAssert {

    public void assertPositive(int value, String message) {
        if (value <= 0) {
            failWithMessage(message + ": expected positive but was " + value);
        }
    }

    public void assertInRange(int value, int min, int max, String message) {
        if (value < min || value > max) {
            failWithMessage(message + ": expected in range [" + min + ", " + max + "] but was " + value);
        }
    }

    private void failWithMessage(String message) {
        try {
            fail(message);
        } catch (AssertionError e) {
            // SoftAssert will collect this exception
            throw e;
        }
    }
}

Usage example:

import org.testng.annotations.Test;

public class CustomSoftAssertExample {

    @Test
    public void testCustomAssertions() {
        CustomSoftAssert softAssert = new CustomSoftAssert();

        softAssert.assertPositive(5, "Value should be positive");
        softAssert.assertPositive(-1, "This will fail but continue");
        softAssert.assertInRange(7, 1, 10, "Value should be in range");
        softAssert.assertInRange(15, 1, 10, "This will also fail but continue");

        softAssert.assertAll(); // Reports all failures
    }
}

Custom Assertions with Java 21

Java 21 features can enhance your assertion capabilities. Let's explore some examples:

Record Pattern Assertions

Using Java 21's record patterns for more expressive assertions:

import org.testng.Assert;
import org.testng.annotations.Test;

public class RecordPatternAssertionExample {

    record Point(int x, int y) {}
    record Rectangle(Point topLeft, Point bottomRight) {}

    @Test
    public void testRectangleProperties() {
        Rectangle rectangle = new Rectangle(new Point(1, 2), new Point(5, 6));

        // Using record pattern for destructuring in assertions
        if (rectangle instanceof Rectangle(Point(int x1, int y1), Point(int x2, int y2))) {
            Assert.assertEquals(x1, 1, "Top-left x coordinate should be 1");
            Assert.assertEquals(y1, 2, "Top-left y coordinate should be 2");
            Assert.assertEquals(x2, 5, "Bottom-right x coordinate should be 5");
            Assert.assertEquals(y2, 6, "Bottom-right y coordinate should be 6");

            // Calculate and assert width and height
            int width = x2 - x1;
            int height = y2 - y1;
            Assert.assertEquals(width, 4, "Rectangle width should be 4");
            Assert.assertEquals(height, 4, "Rectangle height should be 4");
        } else {
            Assert.fail("Record pattern matching failed");
        }
    }
}

Pattern Matching for Switch in Assertions

Using Java 21's pattern matching for switch to create more flexible assertions:

import org.testng.Assert;
import org.testng.annotations.Test;

public class PatternMatchingSwitchAssertionExample {

    sealed interface Shape permits Circle, Rectangle, Triangle {}
    record Circle(double radius) implements Shape {}
    record Rectangle(double width, double height) implements Shape {}
    record Triangle(double base, double height) implements Shape {}

    @Test
    public void testShapeArea() {
        Shape[] shapes = {
            new Circle(5.0),
            new Rectangle(4.0, 5.0),
            new Triangle(3.0, 4.0)
        };

        for (Shape shape : shapes) {
            double expectedArea = switch (shape) {
                case Circle c -> Math.PI * c.radius() * c.radius();
                case Rectangle r -> r.width() * r.height();
                case Triangle t -> 0.5 * t.base() * t.height();
            };

            double actualArea = calculateArea(shape);
            Assert.assertEquals(actualArea, expectedArea, 0.001,
                               "Area calculation for " + shape.getClass().getSimpleName() + " is incorrect");
        }
    }

    private double calculateArea(Shape shape) {
        return switch (shape) {
            case Circle c -> Math.PI * c.radius() * c.radius();
            case Rectangle r -> r.width() * r.height();
            case Triangle t -> 0.5 * t.base() * t.height();
        };
    }
}

Virtual Thread-Based Parallel Assertions

Using Java 21's virtual threads to perform assertions in parallel:

import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.ArrayList;
import java.util.List;

public class ParallelAssertionExample {

    @Test
    public void testParallelAssertions() throws Exception {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<Boolean>> futures = new ArrayList<>();

            // Submit assertion tasks to run in parallel
            for (Integer number : numbers) {
                futures.add(executor.submit(() -> {
                    // Perform some time-consuming validation
                    Thread.sleep(100);

                    boolean isPrime = isPrime(number);
                    boolean isEven = number % 2 == 0;

                    // Assertions for this number
                    if (number == 2) {
                        Assert.assertTrue(isPrime, number + " should be prime");
                        Assert.assertTrue(isEven, number + " should be even");
                    } else if (isEven) {
                        Assert.assertFalse(isPrime, number + " should not be prime");
                    } else if (number > 2) {
                        // For odd numbers > 2, check primality
                        if (number == 3 || number == 5 || number == 7) {
                            Assert.assertTrue(isPrime, number + " should be prime");
                        } else {
                            Assert.assertFalse(isPrime, number + " should not be prime");
                        }
                    }

                    return true; // Task completed successfully
                }));
            }

            // Wait for all assertion tasks to complete
            for (Future<Boolean> future : futures) {
                future.get(); // This will throw an exception if any assertion failed
            }
        }
    }

    private boolean isPrime(int number) {
        if (number <= 1) return false;
        if (number <= 3) return true;
        if (number % 2 == 0 || number % 3 == 0) return false;

        for (int i = 5; i * i <= number; i += 6) {
            if (number % i == 0 || number % (i + 2) == 0) return false;
        }

        return true;
    }
}