Description
WebTestClient
has a special case when a Void
response is expected. Method WebTestClient.ResponseSpec#returnResult(java.lang.Class<T>)
has this documentation comment:
Note that when {@code Void.class} is passed in, the response body is consumed and released.
The same special case logic is not applied to the overloaded version of this method: WebTestClient.ResponseSpec#returnResult(ParameterizedTypeReference<T>)
. This is a bit unexpected; and caused a memory leak in my tests when I swapped a Class parameter for a ParameterizedTypeReference parameter. The following sample code shows the problem (additional GC is forced to make the leak happen faster; netty's leak detection happens on GC):
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.netty.http.client.HttpClient;
class Scratch {
public static void main(String[] args) throws Exception {
var client = WebTestClient.bindToServer(new ReactorClientHttpConnector(HttpClient.create()))
.build();
var b = client.get().uri("http://localhost:8000");
long count = 0;
while(true) {
count += 1;
if (count % 10000 == 0) {
System.out.printf("Done %d requests forcing a G\n", count);
System.gc();
}
b.exchange()
.expectStatus().isEqualTo(200)
//.returnResult(Void.class); // <-- doesn't leak
.returnResult(ParameterizedTypeReference.forType(Void.class)); // <-- leaks
}
}
}
The workaround is to use the Class
signature, or consume the response body some other way using e.g. by specifying .consumeWith(...)
.