How do I compare objects in JUnit tests?

In JUnit tests, you usually compare objects with assertions, not with ==.

The most common choices are:

  • assertEquals(expected, actual) — compares objects using their equals() method.
  • assertSame(expected, actual) — checks whether both references point to the same object.
  • AssertJ’s assertThat(actual).isEqualTo(expected) — a more fluent alternative.
  • Field-by-field assertions — useful when you only care about some properties.

1. Comparing Objects with assertEquals()

In JUnit 5, use:

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class PersonTest {

    @Test
    void shouldCompareObjectsUsingEquals() {
        Person expected = new Person("Alice", 30);
        Person actual = new Person("Alice", 30);

        assertEquals(expected, actual);
    }
}

This works only if Person correctly overrides equals().

Example:

import java.util.Objects;

public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (!(o instanceof Person person)) {
            return false;
        }

        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

Now two different Person instances with the same values are considered equal.

2. Do Not Use == for Value Comparison

This checks whether both variables refer to the same object in memory:

Person p1 = new Person("Alice", 30);
Person p2 = new Person("Alice", 30);

assertEquals(p1, p2); // compares values using equals()

But this would fail:

import static org.junit.jupiter.api.Assertions.assertTrue;

assertTrue(p1 == p2); // false, because they are different objects

Use == only when you intentionally want to check reference identity.

3. Use assertSame() for Reference Comparison

If you want to verify that two references point to the exact same object, use assertSame():

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertSame;

class PersonTest {

    @Test
    void shouldReferToSameObject() {
        Person person = new Person("Alice", 30);

        Person samePerson = person;

        assertSame(person, samePerson);
    }
}

Use assertSame() for identity checks, not regular value comparison.

4. Compare Individual Fields

Sometimes you do not need full object equality. You may only care about specific fields:

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class PersonTest {

    @Test
    void shouldComparePersonFields() {
        Person actual = new Person("Alice", 30);

        assertEquals("Alice", actual.getName());
        assertEquals(30, actual.getAge());
    }
}

This is useful when:

  • the class does not override equals(),
  • only some fields matter,
  • generated fields like id, timestamps, or audit fields should be ignored.

5. Use AssertJ for More Readable Object Assertions

AssertJ provides fluent assertions:

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class PersonTest {

    @Test
    void shouldCompareObjectsWithAssertJ() {
        Person expected = new Person("Alice", 30);
        Person actual = new Person("Alice", 30);

        assertThat(actual).isEqualTo(expected);
    }
}

You can also compare specific properties:

assertThat(actual)
        .extracting(Person::getName, Person::getAge)
        .containsExactly("Alice", 30);

Or compare objects recursively:

assertThat(actual)
        .usingRecursiveComparison()
        .isEqualTo(expected);

Recursive comparison is helpful when objects contain nested objects and you do not want to rely on every class implementing equals().

6. Comparing Lists of Objects

For lists, assertEquals() also uses equals() on each element:

import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

class PersonListTest {

    @Test
    void shouldCompareListsOfPeople() {
        List<Person> expected = List.of(
                new Person("Alice", 30),
                new Person("Bob", 25)
        );

        List<Person> actual = List.of(
                new Person("Alice", 30),
                new Person("Bob", 25)
        );

        assertEquals(expected, actual);
    }
}

The order must match. If order does not matter, AssertJ is often clearer:

assertThat(actual)
        .containsExactlyInAnyOrderElementsOf(expected);

7. Common Rule of Thumb

Use this:

assertEquals(expected, actual);

when you want to compare object values.

Use this:

assertSame(expected, actual);

when you want to check that both variables refer to the exact same object.

Avoid this for object value comparison:

expected == actual

because it checks identity, not logical equality.

Summary

Goal Recommended Assertion
Compare object values assertEquals(expected, actual)
Compare object references assertSame(expected, actual)
Compare selected fields assertEquals(expectedName, actual.getName())
Compare nested objects without relying on equals() AssertJ usingRecursiveComparison()
Compare lists in order assertEquals(expectedList, actualList)
Compare lists ignoring order AssertJ containsExactlyInAnyOrderElementsOf()

In most JUnit tests, object comparison starts with assertEquals(expected, actual), as long as the class has a proper equals() implementation.