Suppose we have this code in production:
private boolean isExternalProfileEnabled() {
return environment.acceptsProfiles(Profiles.of("external"));
}
Then I want to mock this in tests and I write something like:
when(environment.acceptsProfiles(Profiles.of("external"))).thenReturn(true);
And at runtime the mock fails to return true
, because comparison of arguments fails due to missing equals()
and hashCode()
implementations in ParsedProfiles
.
This makes us use the deprecated API and rewrite the example code like:
private boolean isExternalProfileEnabled() {
return environment.acceptsProfiles("external");
}
and use of the deprecated API in turn makes Sonar unhappy.
Comment From: jhoeller
@sbrannen Let's backport this to 5.1.x as well since that specific Profiles
API got introduced in 5.1.
Comment From: sbrannen
@jhoeller, I was literally just about to discuss this with you.
It turns out it's not as simple as it may appear at a quick glance.
I've experimented locally but haven't checked anything into a branch.
In summary...
In order to properly implement equals()
and hashCode()
we might have to break down the individual expressions using something like an AST in order to determine the equivalence between logically equivalent expressions such as spring & java
and java & spring
. The same applies for !
and |
support.
Currently, ParsedProfiles
stores each expression as a single String
. So we cannot reliably use that in any robust way. It works if the tokens of the expressions are specified in the same order. For example, spring&java
and spring & java
can easily be checked to be equivalent (ignoring all whitespace -- and I have that working locally); whereas, without analyzing the meaning of the expression, spring & java
and java & spring
cannot be determined to be logically equivalent. It's even more complex than that due to the tree-based structure of expressions (e.g., (spring & framework) | (spring & java)
).
Now... the information is present in the internal Profiles[] parsed
field, but that's not a data structure that we can query. The lambda expression returned by org.springframework.core.env.ProfilesParser.equals(String)
captures the individual token (e.g., "spring" or "java"). So again the information is there, but not in any form that we can access for comparison.
Let's discuss our options...
Comment From: jhoeller
Maybe we should simply restrict it to literal equality of the input String... probably good enough for the mocking scenario here. We could even preserve whitespace in the comparison, avoiding any special processing of the String for comparison purposes.