skip to content

Explain the difference between assertSame and assertEquals, and give a case where each is correct.

level: middleimportance: should knowfreq 55%

answer

  1. assertEquals = == (structural)
  2. assertSame = === (reference identity)
  3. assertNotSame is the inverse
  4. use assertSame for cache/pool/singleton
  5. don't assertSame value types (interning)

basics

~10 s

assertEquals checks two values are equal by content (==). assertSame checks they are the exact same object in memory (===). Use assertEquals for value comparison, assertSame to confirm something returned the very same instance.

solid answer

~30 s

`assertEquals` uses structural equality (`==` → `.equals()`): two distinct objects with equal content pass. `assertSame` uses referential identity (`===`): only the same heap instance passes; `assertNotSame` is its inverse. Use `assertEquals` for normal value checks (data classes, strings, collections). Use `assertSame` when identity is the contract — a cache or object pool returning the same instance, a singleton, an interned object, or a fluent API returning `this`. A pitfall: small `Int`s and string literals may be interned, so `assertSame` can pass deceptively for value types; never use it to compare values. Both share the optional trailing `message` argument.

code

kotlin · 7 lines
kotlin
import kotlin.test.*

val a = listOf(1, 2)
val b = listOf(1, 2)
assertEquals(a, b)    // PASS: same content
assertNotSame(a, b)   // PASS: different instances
assertSame(a, a)      // PASS: identical reference

go deeper

for a junior

Knows assertEquals compares content; vaguely aware assertSame is about identity.

for a middle

Clearly distinguishes == vs === and names a correct use case for each.

for a senior

Warns about interning pitfalls and reserves assertSame for genuine identity contracts (cache/pool/singleton/this).

for a principal

Discusses how identity assertions encode invariants in API contracts and the risk of brittle tests when identity isn't actually guaranteed.

## `assertSame` vs `assertEquals` - `assertEquals(expected, actual)` checks **structural** equality with `==` (calls `.equals()`). - `assertSame(expected, actual)` checks **referential** identity with `===` — both references must point to the *same object instance* on the heap. - `assertNotSame(illegal, actual)` is the inverse: fails if they are the same instance. ```kotlin import kotlin.test.assertSame import kotlin.test.assertEquals import kotlin.test.assertNotSame val a = listOf(1, 2) val b = listOf(1, 2) assertEquals(a, b) // PASS: structurally equal assertNotSame(a, b) // PASS: different instances // assertSame(a, b) // FAIL: not the same reference val shared = a assertSame(a, shared) // PASS: same instance ``` ### When to reach for `assertSame` - Verifying a cache / object pool returns the **same** instance. - Verifying a singleton or interned object identity. - Asserting a function returns `this` for fluent chaining. ### Gotcha: identity of boxed primitives / interned values Small `Int` values and string literals may be interned, so `assertSame` can pass "by accident" for cached values and fail for larger ones. Don't use `assertSame` to compare value types — use `assertEquals`. Reserve `assertSame` strictly for genuine reference-identity intent.

  • Why can assertSame(1, 1) pass but assertSame(1000, 1000) be fragile?
    The JVM caches small boxed Integers (typically -128..127), so identity may hold for small values and not larger ones — exactly why you shouldn't use assertSame on value types.
  • Give a real test where assertSame is the right tool.
    Asserting a memoizing cache returns the same object on a second call: `assertSame(cache.get(k), cache.get(k))`.

assertEquals: two twins look identical. assertSame: it's literally the same person in two photos.

saying these in an interview costs you the question

  • Says assertSame and assertEquals are interchangeable
  • Thinks assertSame compares content
  • Uses assertSame to compare Ints/Strings as a habit
  • Confuses === and == directions
  • Cannot give an identity use case

context