Closed martinfrancois closed 1 year ago
Technically speaking, assertSame(128, 128)
works as expected.
The Javadoc clearly states the following:
Assert that expected and actual refer to the same object.
And the error message even shows that there are two distinct Integer
objects.
org.opentest4j.AssertionFailedError: expected: java.lang.Integer@7995092a<128> but was: java.lang.Integer@7fc2413d<128>
If we want assertSame(128, 128)
to pass, we would have to introduce an overloaded assertSame(int, int)
method, and we would also need to introduce overloaded variants of assertSame()
for other primitive types.
So the question to answer is whether we want to introduce those overloaded variants.
Technically speaking you are correct, yes. Good point on the error message, I can still imagine developers unaware of the caching would be confused about why two of the seemingly same primitive values result in different references in that case, which could mislead them into thinking their implementation is wrong and a lot of time wasted in debugging.
In the end it comes down to whether JUnit users would expect assertSame
to have overloaded variants, as you mentioned. Personally, I couldn't think of any case where a developer would on purpose write code using primitive values where they would make use of the fact that 127 always references the same object, while 128 doesn't. I would consider this to be bad design as it is very implicit, yet this would be the only case I can think of where not having overloaded assertSame
methods would be expected.
I could be missing something of course, but I think it would hurt JUnit users more not to have overloaded assertSame
methods for primitive types than to have them.
I could be missing something of course, but I think it would hurt JUnit users more not to have overloaded
assertSame
methods for primitive types than to have them.
We'll likely discuss this topic within the team during an upcoming team call; however, if someone invokes assertSame(128, 128)
, they are effectively using the API wrong.
assertSame()
is only intended to check object reference identity. It does not check equality, and in Java it is important to understand the difference between identity and equality.
For equality, we have assertEquals(int, int)
, and assertEquals(128, 128)
passes.
If we do decide to introduce primitive type overloads for assertSame()
, each of those overloads would simply delegate to the corresponding assertEquals()
method, which seems a bit unnecessary to me. However, I suppose it could be beneficial to inexperienced Java developers who don't yet understand the difference between equality and identity (and who possibly are not aware of Java's support for auto-boxing).
As a side note, assertSame(new Integer(127), new Integer(127))
also fails (for hopefully obvious reasons).
assertSame
is not intended to be used with primitive types. Can you use assertEquals
instead?
Team decision: Document that assertSame
should not be used with primitive types.
assertSame()
is only intended to check object reference identity. It does not check equality, and in Java it is important to understand the difference between identity and equality.
Agreed. The ==
operator in Java is often associated with checking for identity and you typically also use it to check for equality between primitive data types. Given equals
is exclusively used for checking the equality of two objects and ==
is typically used to compare identity, I think it's not unexpected for developers who start learning JUnit intuitively to start associating equals
with assertEquals
and ==
with assertSame
and to always use them as such, even though this technically means you would be using the API wrong, as you were pointing out. Then, as 127 == 127
is true
, just like 128 == 128
is, I don't think it's too far off to expect assertSame
to work in the same way.
assertSame
is not intended to be used with primitive types. Can you useassertEquals
instead?
In most cases I would say yes, unless you want to verify a primitive datatype is used, as assertEquals
will still pass in case of assertEquals(new Integer(128), new Integer(128))
for example, which is why I tended to use assertSame
in those cases in the past.
Team decision: Document that
assertSame
should not be used with primitive types.
This was one approach I was thinking of as well. One option to take it further could be to throw an UnsupportedOperationException
if assertSame
is attempted to be used with primitive data types to make sure it is not used in the wrong way.
One option to take it further could be to throw an
UnsupportedOperationException
ifassertSame
is attempted to be used with primitive data types to make sure it is not used in the wrong way.
That's not possible without introducing overloaded variants for all affected primitive types.
I have changed this issue's title to align with the current action item.
Steps to reproduce
Run the following Test Case:
Context
This is a similar issue to: https://github.com/assertj/assertj/issues/2887
It appears to be the same kind of autoboxing caching issue due to
Object
being used inassertSame
. This also means that, as mentioned in the issue, it not only affects this exact case, but also any case mentioned in JLS section 5.1.7:This means that for example, the following case is also affected:
Deliverables