Batu Lab NotesPractical developer guides

Keep custom equality assertions from hiding object identity bugs

By Batu ยท English technical notes

Keep custom equality assertions from hiding object identity bugs

Use assertEqual for a value contract and assertIs when the contract requires the same object instance. The two MutableSetting objects below compare equal because their names match, yet assertIs correctly rejects them as separate instances. Conversely, the DEFAULT_SETTING sentinel is returned unchanged, so identity is the meaningful assertion for that singleton-style contract.

Python distinguishes equality from identity: an __eq__ method may make different objects compare equal, while is tests whether two references denote the same object. The Python assertIs documentation specifies that the assertion succeeds only when its two arguments are the same object. This matters when callers must share a cache entry, sentinel, lock, or mutable configuration object rather than merely receive an equivalent replacement.

The fixture keeps its custom objects small and does not mutate them after comparison. That removes mutation timing as an alternative explanation for the result. It captures the expected identity assertion failure without printing its implementation-dependent message, then verifies the intended alias with assertIs. Do not replace ordinary value assertions with identity assertions indiscriminately: equal but independently allocated objects are often exactly what an API should return. These unittest assertions are available on supported Python versions.

AI assistance disclosure: this article was drafted with AI assistance and uses synthetic in-memory objects.

import unittest

class MutableSetting:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return isinstance(other, MutableSetting) and self.name == other.name

DEFAULT_SETTING = MutableSetting("standard")

def default_setting():
    return DEFAULT_SETTING

case = unittest.TestCase()
left = MutableSetting("standard")
right = MutableSetting("standard")

case.assertEqual(left, right)
with case.assertRaises(AssertionError):
    case.assertIs(left, right)

provided = default_setting()
case.assertIs(provided, DEFAULT_SETTING)

print(f"equal values: {left == right}")
print(f"same instance for equal values: {left is right}")
print(f"same singleton: {provided is DEFAULT_SETTING}")
equal values: True
same instance for equal values: False
same singleton: True