-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrong MockRestRequestMatchers.header() method in spring-test being invoked (JDK issue?) #30220
Comments
That's a bit unfortunate indeed 😞 To be precise, this is because the signature is really:
So, to the compiler, Why was the new variant introduced?See initial context in #28660. Note that the vararg based signature, which takes
The signature has to use A better workaroundYour workaround of calling .andExpect(header("X-MY-HEADER",
Machers.contains("myValue")
//equivalent to Matchers.contains(CoreMatchers.equalTo("myValue"))
)); This is unambiguously a Can this be fixed in Framework?I have toyed with modifying the list-based overload implementation to try to detect such "unexpected" matchers and apply the passed This would result in the following puzzling statements all passing in tests: request.getHeaders().put("twoElements", List.of("a", "b"));
MockRestRequestMatchers.header("twoElements", Matchers.equalTo("a")).match(this.request);
MockRestRequestMatchers.header("twoElements", Matchers.equalTo(List.of("a", "b"))).match(this.request);
MockRestRequestMatchers.header("twoElements", Matchers.hasToString("[a, b]")).match(this.request);
MockRestRequestMatchers.header("twoElements", Matchers.hasToString("a")).match(this.request); This could also short-circuit null checks (although this example is a bit contrived, nonetheless request.getHeaders().add("m", null);
request.getHeaders().add("m", "b");
MockRestRequestMatchers.header("m", Matchers.notNullValue()).match(this.request);
MockRestRequestMatchers.header("m", Matchers.nullValue()).match(this.request); (it passes because the |
This can be considered a form of regression and the feature is recent enough that we might consider hot-fixing the API, renaming the list-based variant to something unambiguously different like |
This commit changes the name of two recently introduced methods in the `MockRestRequestMatchers` class for header and queryParam. These have been found to cause false negatives in user tests, due to the new overload taking precedence in some cases. Namely, using a `Matcher` factory method which can apply to both `List` and `String` will cause the compiler to select the newest list overload, by instantiating a `Matcher<Object>`. This can cause false negatives in user tests, failing tests that used to pass because the Matcher previously applied to the first String in the header or queryParam value list. For instance, `equalsTo("a")`. The new overloads are recent enough and this has enough potential to cause an arbitrary number of user tests to fail that we break the API to eliminate the ambiguity, by renaming the methods with a `*List` suffix. Closes spring-projectsgh-30220 Close spring-projectsgh-30238 See spring-projectsgh-29953 See spring-projectsgh-28660
Hi @simonbasle. Thanks a lot for the detailed explanation, that is very informative: I had missed the possibility that type inference could use I can see that you've opted to rename the new methods to add the Once again, thanks for addressing this so quickly. |
This commit changes the name of two recently introduced methods in the `MockRestRequestMatchers` class for header and queryParam. These have been found to cause false negatives in user tests, due to the new overload taking precedence in some cases. Namely, using a `Matcher` factory method which can apply to both `List` and `String` will cause the compiler to select the newest list overload, by instantiating a `Matcher<Object>`. This can cause false negatives in user tests, failing tests that used to pass because the Matcher previously applied to the first String in the header or queryParam value list. For instance, `equalsTo("a")`. The new overloads are recent enough and this has enough potential to cause an arbitrary number of user tests to fail that we break the API to eliminate the ambiguity, by renaming the methods with a `*List` suffix. Closes gh-30220 Closes gh-30238 See gh-29953 See gh-28660
This commit changes the name of two recently introduced methods in the `MockRestRequestMatchers` class for header and queryParam. These have been found to cause false negatives in user tests, due to the new overload taking precedence in some cases. Namely, using a `Matcher` factory method which can apply to both `List` and `String` will cause the compiler to select the newest list overload, by instantiating a `Matcher<Object>`. This can cause false negatives in user tests, failing tests that used to pass because the Matcher previously applied to the first String in the header or queryParam value list. For instance, `equalsTo("a")`. The new overloads are recent enough and this has enough potential to cause an arbitrary number of user tests to fail that we break the API to eliminate the ambiguity, by renaming the methods with a `*List` suffix. See gh-30220 See gh-30238 Closes gh-30235
Affects: 2.7.10
Hello everyone.
I'm uncertain if this is an issue with spring-test or with Java compiler itself but, from Spring Boot 2.7.10 (which incorporates spring-test 5.3.26), we have been having an odd issue with tests calling the wrong
MockRestRequestMatchers.header()
method.In spring-test 5.3.25 there were a couple of
header()
methods, but the one that interest us is the one with this signature:public static RequestMatcher header(String name, Matcher<? super String>... matchers)
this was used in testing like this:
.andExpect(header("X-MY-HEADER", equalTo("myValue")))
where
equalTo()
is defined in Hamcrest aspublic static <T> Matcher<T> equalTo(T operand)
this worked normally.
From Spring Boot 2.7.10 (spring-test 5.3.26) a new
header()
method has been added with this signature:public static RequestMatcher header(String name, Matcher<? super List<String>> matcher)
and this is where things are breaking: the same call above is now entering this new method, as opposed to the previous. I find this surprising because the signature doesn't match,
equalTo("myValue")
returnsMatcher<String>
, which doesn't align toMatcher<? super List<String>>
, and yet it's coming in. If I change the call toheader()
to explicitly call out the type like this:.andExpect(header("X-MY-HEADER", CoreMatchers.<String>equalTo("myValue")))
then things go back to normal and the call is again directed to the old
header()
method.I'm guessing that, via type erasure, the signatures of the two
header()
methods are being simplified to justheader(String, Matcher)
and, there being two possible candidates that can match the call, Java is just picking the first one. I'm not sure what could be done from spring-framework's perspective, though.Thoughts?
The text was updated successfully, but these errors were encountered: