Browse Source

Refactor @ResponseBody and ResponseEntity tests

Introduce separate test classes for each base class in the hierarchy
above @ResponseBody and ResponseEntity result handlers.

Also start porting existing unit test cases for @ResponseBody and
ResponseEntity return value handlers.
pull/1111/head
Rossen Stoyanchev 10 years ago
parent
commit
cae8800183
  1. 2
      spring-web-reactive/src/main/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupport.java
  2. 13
      spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageConverterResultHandler.java
  3. 2
      spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java
  4. 137
      spring-web-reactive/src/test/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupportTests.java
  5. 155
      spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageConverterResultHandlerTests.java
  6. 147
      spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java
  7. 157
      spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java

2
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupport.java

@ -126,7 +126,7 @@ public abstract class ContentNegotiatingResultHandlerSupport implements Ordered
} }
private List<MediaType> getAcceptableTypes(ServerWebExchange exchange) { private List<MediaType> getAcceptableTypes(ServerWebExchange exchange) {
List<MediaType> mediaTypes = this.contentTypeResolver.resolveMediaTypes(exchange); List<MediaType> mediaTypes = getContentTypeResolver().resolveMediaTypes(exchange);
return (mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes); return (mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes);
} }

13
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageConverterResultHandler.java

@ -19,6 +19,7 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
@ -82,16 +83,22 @@ public abstract class AbstractMessageConverterResultHandler extends ContentNegot
publisher = Mono.empty(); publisher = Mono.empty();
} }
elementType = bodyType.getGeneric(0); elementType = bodyType.getGeneric(0);
if (Void.class.equals(elementType.getRawClass())) {
return Mono.from((Publisher<Void>) publisher);
}
} }
else { else {
publisher = Mono.justOrEmpty(body); publisher = Mono.justOrEmpty(body);
elementType = bodyType; elementType = bodyType;
} }
if (Void.class.equals(elementType.getRawClass())) {
return Mono.from((Publisher<Void>) publisher);
}
List<MediaType> producibleTypes = getProducibleMediaTypes(elementType); List<MediaType> producibleTypes = getProducibleMediaTypes(elementType);
if (producibleTypes.isEmpty()) {
return Mono.error(new IllegalStateException(
"No converter for return value type: " + elementType));
}
MediaType bestMediaType = selectMediaType(exchange, producibleTypes); MediaType bestMediaType = selectMediaType(exchange, producibleTypes);
if (bestMediaType != null) { if (bestMediaType != null) {

2
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java

@ -104,7 +104,7 @@ public class ResponseEntityResultHandler extends AbstractMessageConverterResultH
if (!entityHeaders.isEmpty()) { if (!entityHeaders.isEmpty()) {
entityHeaders.entrySet().stream() entityHeaders.entrySet().stream()
.filter(entry -> responseHeaders.containsKey(entry.getKey())) .filter(entry -> !responseHeaders.containsKey(entry.getKey()))
.forEach(entry -> responseHeaders.put(entry.getKey(), entry.getValue())); .forEach(entry -> responseHeaders.put(entry.getKey(), entry.getValue()));
} }

137
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupportTests.java

@ -0,0 +1,137 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.web.reactive.accept.FixedContentTypeResolver;
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.springframework.http.MediaType.ALL;
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
import static org.springframework.http.MediaType.IMAGE_GIF;
import static org.springframework.http.MediaType.IMAGE_JPEG;
import static org.springframework.http.MediaType.IMAGE_PNG;
import static org.springframework.http.MediaType.TEXT_PLAIN;
import static org.springframework.web.reactive.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
/**
* Unit tests for {@link ContentNegotiatingResultHandlerSupport}.
* @author Rossen Stoyanchev
*/
public class ContentNegotiatingResultHandlerSupportTests {
private TestHandlerSupport handlerSupport;
private MockServerHttpRequest request;
private ServerWebExchange exchange;
@Before
public void setUp() throws Exception {
this.handlerSupport = new TestHandlerSupport();
this.request = new MockServerHttpRequest(HttpMethod.GET, new URI("/path"));
this.exchange = new DefaultServerWebExchange(
this.request, new MockServerHttpResponse(), mock(WebSessionManager.class));
}
@Test
public void usesContentTypeResolver() throws Exception {
RequestedContentTypeResolver resolver = new FixedContentTypeResolver(IMAGE_GIF);
TestHandlerSupport handlerSupport = new TestHandlerSupport(resolver);
List<MediaType> mediaTypes = Arrays.asList(IMAGE_JPEG, IMAGE_GIF, IMAGE_PNG);
MediaType actual = handlerSupport.selectMediaType(this.exchange, mediaTypes);
assertEquals(IMAGE_GIF, actual);
}
@Test
public void producibleMediaTypesRequestAttribute() throws Exception {
Set<MediaType> producible = Collections.singleton(IMAGE_GIF);
this.exchange.getAttributes().put(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, producible);
List<MediaType> mediaTypes = Arrays.asList(IMAGE_JPEG, IMAGE_GIF, IMAGE_PNG);
MediaType actual = handlerSupport.selectMediaType(this.exchange, mediaTypes);
assertEquals(IMAGE_GIF, actual);
}
@Test // SPR-9160
public void sortsByQuality() throws Exception {
this.request.getHeaders().add("Accept", "text/plain; q=0.5, application/json");
List<MediaType> mediaTypes = Arrays.asList(TEXT_PLAIN, APPLICATION_JSON_UTF8);
MediaType actual = this.handlerSupport.selectMediaType(this.exchange, mediaTypes);
assertEquals(APPLICATION_JSON_UTF8, actual);
}
@Test
public void charsetFromAcceptHeader() throws Exception {
MediaType text8859 = MediaType.parseMediaType("text/plain;charset=ISO-8859-1");
MediaType textUtf8 = MediaType.parseMediaType("text/plain;charset=UTF-8");
this.request.getHeaders().setAccept(Collections.singletonList(text8859));
MediaType actual = this.handlerSupport.selectMediaType(this.exchange, Collections.singletonList(textUtf8));
assertEquals(text8859, actual);
}
@Test // SPR-12894
public void noConcreteMediaType() throws Exception {
List<MediaType> producible = Collections.singletonList(ALL);
MediaType actual = this.handlerSupport.selectMediaType(this.exchange, producible);
assertEquals(APPLICATION_OCTET_STREAM, actual);
}
private static class TestHandlerSupport extends ContentNegotiatingResultHandlerSupport {
protected TestHandlerSupport() {
this(new HeaderContentTypeResolver());
}
public TestHandlerSupport(RequestedContentTypeResolver contentTypeResolver) {
super(new GenericConversionService(), contentTypeResolver);
}
}
}

155
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageConverterResultHandlerTests.java

@ -0,0 +1,155 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.test.TestSubscriber;
import rx.Observable;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Decoder;
import org.springframework.core.codec.Encoder;
import org.springframework.core.codec.support.ByteBufferDecoder;
import org.springframework.core.codec.support.ByteBufferEncoder;
import org.springframework.core.codec.support.JacksonJsonDecoder;
import org.springframework.core.codec.support.JacksonJsonEncoder;
import org.springframework.core.codec.support.Jaxb2Decoder;
import org.springframework.core.codec.support.Jaxb2Encoder;
import org.springframework.core.codec.support.JsonObjectDecoder;
import org.springframework.core.codec.support.StringDecoder;
import org.springframework.core.codec.support.StringEncoder;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
import org.springframework.http.converter.reactive.HttpMessageConverter;
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.ObjectUtils;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link AbstractMessageConverterResultHandler}.
* @author Rossen Stoyanchev
*/
public class MessageConverterResultHandlerTests {
private AbstractMessageConverterResultHandler resultHandler;
private MockServerHttpResponse response = new MockServerHttpResponse();
private ServerWebExchange exchange;
@Before
public void setUp() throws Exception {
this.resultHandler = createResultHandler();
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/path"));
this.exchange = new DefaultServerWebExchange(request, this.response, mock(WebSessionManager.class));
}
@Test // SPR-12894
@Ignore // GH # 121
public void useDefaultContentType() throws Exception {
Object body = new ByteArrayResource("body".getBytes("UTF-8"));
ResolvableType bodyType = ResolvableType.forType(Resource.class);
this.resultHandler.writeBody(this.exchange, body, bodyType).block(Duration.ofSeconds(5));
assertEquals("image/jpeg", this.response.getHeaders().getFirst("Content-Type"));
TestSubscriber.subscribe(this.response.getBody())
.assertValuesWith(buf -> assertEquals("body",
DataBufferTestUtils.dumpString(buf, Charset.forName("UTF-8"))));
}
@Test
public void voidReturnType() throws Exception {
testVoidReturnType(null, ResolvableType.forType(Void.class));
testVoidReturnType(Mono.empty(), ResolvableType.forClassWithGenerics(Mono.class, Void.class));
testVoidReturnType(Flux.empty(), ResolvableType.forClassWithGenerics(Flux.class, Void.class));
testVoidReturnType(Observable.empty(), ResolvableType.forClassWithGenerics(Observable.class, Void.class));
}
private void testVoidReturnType(Object body, ResolvableType bodyType) {
this.resultHandler.writeBody(this.exchange, body, bodyType).block(Duration.ofSeconds(5));
assertNull(this.response.getHeaders().get("Content-Type"));
assertNull(this.response.getBody());
}
@Test // SPR-13135
public void unsupportedReturnType() throws Exception {
ByteArrayOutputStream body = new ByteArrayOutputStream();
ResolvableType bodyType = ResolvableType.forType(OutputStream.class);
HttpMessageConverter<?> converter = new CodecHttpMessageConverter<>(new ByteBufferEncoder());
Mono<Void> mono = createResultHandler(converter).writeBody(this.exchange, body, bodyType);
TestSubscriber.subscribe(mono).assertError(IllegalStateException.class);
}
private AbstractMessageConverterResultHandler createResultHandler(HttpMessageConverter<?>... converters) {
List<HttpMessageConverter<?>> converterList;
if (ObjectUtils.isEmpty(converters)) {
converterList = new ArrayList<>();
converterList.add(new CodecHttpMessageConverter<>(new ByteBufferEncoder()));
converterList.add(new CodecHttpMessageConverter<>(new StringEncoder()));
converterList.add(new ResourceHttpMessageConverter());
converterList.add(new CodecHttpMessageConverter<>(new Jaxb2Encoder()));
converterList.add(new CodecHttpMessageConverter<>(new JacksonJsonEncoder()));
}
else {
converterList = Arrays.asList(converters);
}
GenericConversionService service = new GenericConversionService();
service.addConverter(new ReactiveStreamsToCompletableFutureConverter());
service.addConverter(new ReactiveStreamsToRxJava1Converter());
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
return new AbstractMessageConverterResultHandler(converterList, service, resolver) {};
}
}

147
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java

@ -17,143 +17,154 @@
package org.springframework.web.reactive.result.method.annotation; package org.springframework.web.reactive.result.method.annotation;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.reactivestreams.Publisher;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Encoder; import org.springframework.core.codec.support.ByteBufferEncoder;
import org.springframework.core.codec.support.JacksonJsonEncoder; import org.springframework.core.codec.support.JacksonJsonEncoder;
import org.springframework.core.codec.support.Jaxb2Encoder;
import org.springframework.core.codec.support.StringEncoder; import org.springframework.core.codec.support.StringEncoder;
import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.reactive.CodecHttpMessageConverter; import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
import org.springframework.http.converter.reactive.HttpMessageConverter; import org.springframework.http.converter.reactive.HttpMessageConverter;
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
import org.springframework.http.server.reactive.MockServerHttpRequest; import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.ExtendedModelMap;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.HandlerResultHandler;
import org.springframework.web.reactive.accept.FixedContentTypeResolver;
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager; import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
/** /**
* Unit tests for {@link ResponseBodyResultHandler}. * Unit tests for {@link ResponseBodyResultHandler}.
*
* consider whether the logic under test is in a parent class, then see:
* <ul>
* <li>{@code MessageConverterResultHandlerTests},
* <li>{@code ContentNegotiatingResultHandlerSupportTests}
* </ul>
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class ResponseBodyResultHandlerTests { public class ResponseBodyResultHandlerTests {
private ResponseBodyResultHandler resultHandler;
@Test private MockServerHttpResponse response = new MockServerHttpResponse();
public void supports() throws NoSuchMethodException {
ResponseBodyResultHandler handler = createHandler(new StringEncoder());
TestController controller = new TestController();
HandlerMethod hm = new HandlerMethod(controller, TestController.class.getMethod("notAnnotated")); private ServerWebExchange exchange;
ResolvableType type = ResolvableType.forMethodParameter(hm.getReturnType());
assertFalse(handler.supports(new HandlerResult(hm, null, type, new ExtendedModelMap())));
hm = new HandlerMethod(controller, TestController.class.getMethod("publisherString"));
type = ResolvableType.forMethodParameter(hm.getReturnType());
assertTrue(handler.supports(new HandlerResult(hm, null, type, new ExtendedModelMap())));
hm = new HandlerMethod(controller, TestController.class.getMethod("publisherVoid")); @Before
type = ResolvableType.forMethodParameter(hm.getReturnType()); public void setUp() throws Exception {
assertTrue(handler.supports(new HandlerResult(hm, null, type, new ExtendedModelMap()))); this.resultHandler = createHandler();
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/path"));
this.exchange = new DefaultServerWebExchange(request, this.response, mock(WebSessionManager.class));
} }
@Test
public void defaultOrder() throws Exception {
ResponseBodyResultHandler handler = createHandler(new StringEncoder());
assertEquals(100, handler.getOrder());
}
@Test private ResponseBodyResultHandler createHandler(HttpMessageConverter<?>... converters) {
public void usesContentTypeResolver() throws Exception { List<HttpMessageConverter<?>> converterList;
RequestedContentTypeResolver resolver = new FixedContentTypeResolver(APPLICATION_JSON_UTF8); if (ObjectUtils.isEmpty(converters)) {
HandlerResultHandler handler = createHandler(resolver, new StringEncoder(), new JacksonJsonEncoder()); converterList = new ArrayList<>();
converterList.add(new CodecHttpMessageConverter<>(new ByteBufferEncoder()));
ServerWebExchange exchange = createExchange("/foo"); converterList.add(new CodecHttpMessageConverter<>(new StringEncoder()));
HandlerResult result = new HandlerResult(new Object(), "fooValue", ResolvableType.forClass(String.class)); converterList.add(new ResourceHttpMessageConverter());
handler.handleResult(exchange, result).block(); converterList.add(new CodecHttpMessageConverter<>(new Jaxb2Encoder()));
converterList.add(new CodecHttpMessageConverter<>(new JacksonJsonEncoder()));
}
else {
converterList = Arrays.asList(converters);
}
GenericConversionService service = new GenericConversionService();
service.addConverter(new ReactiveStreamsToCompletableFutureConverter());
service.addConverter(new ReactiveStreamsToRxJava1Converter());
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
assertEquals(APPLICATION_JSON_UTF8, exchange.getResponse().getHeaders().getContentType()); return new ResponseBodyResultHandler(converterList, new DefaultConversionService(), resolver);
} }
@Test @Test
public void detectsProducibleMediaTypesAttribute() throws Exception { public void supports() throws NoSuchMethodException {
ServerWebExchange exchange = createExchange("/foo"); TestController controller = new TestController();
Set<MediaType> mediaTypes = Collections.singleton(MediaType.APPLICATION_JSON); testSupports(controller, "handleReturningString", true);
exchange.getAttributes().put(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes); testSupports(controller, "handleReturningVoid", true);
testSupports(controller, "doWork", false);
HandlerResultHandler handler = createHandler(new StringEncoder(), new JacksonJsonEncoder()); TestRestController restController = new TestRestController();
testSupports(restController, "handleReturningString", true);
testSupports(restController, "handleReturningVoid", true);
}
HandlerResult result = new HandlerResult(new Object(), "fooValue", ResolvableType.forClass(String.class)); private void testSupports(Object controller, String method, boolean result) throws NoSuchMethodException {
handler.handleResult(exchange, result).block(); HandlerMethod hm = handlerMethod(controller, method);
ResolvableType type = ResolvableType.forMethodParameter(hm.getReturnType());
HandlerResult handlerResult = new HandlerResult(hm, null, type, new ExtendedModelMap());
assertEquals(result, this.resultHandler.supports(handlerResult));
}
assertEquals(MediaType.APPLICATION_JSON, exchange.getResponse().getHeaders().getContentType()); @Test
public void defaultOrder() throws Exception {
assertEquals(100, this.resultHandler.getOrder());
} }
private ResponseBodyResultHandler createHandler(Encoder<?>... encoders) { private HandlerMethod handlerMethod(Object controller, String method) throws NoSuchMethodException {
return createHandler(new HeaderContentTypeResolver(), encoders); return new HandlerMethod(controller, controller.getClass().getMethod(method));
} }
private ResponseBodyResultHandler createHandler(RequestedContentTypeResolver resolver,
Encoder<?>... encoders) {
List<HttpMessageConverter<?>> converters = Arrays.stream(encoders) @RestController @SuppressWarnings("unused")
.map(encoder -> new CodecHttpMessageConverter<>(encoder, null)) private static class TestRestController {
.collect(Collectors.toList());
return new ResponseBodyResultHandler(converters, new DefaultConversionService(), resolver);
}
private ServerWebExchange createExchange(String path) throws URISyntaxException { public String handleReturningString() {
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI(path)); return null;
WebSessionManager sessionManager = mock(WebSessionManager.class); }
return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
}
public Void handleReturningVoid() {
return null;
}
}
@SuppressWarnings("unused") @Controller @SuppressWarnings("unused")
private static class TestController { private static class TestController {
public Publisher<String> notAnnotated() { @ResponseBody
public String handleReturningString() {
return null; return null;
} }
@ResponseBody @ResponseBody
public Publisher<String> publisherString() { public Void handleReturningVoid() {
return null; return null;
} }
@ResponseBody public String doWork() {
public Publisher<Void> publisherVoid() {
return null; return null;
} }
} }
} }

157
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java

@ -16,134 +16,139 @@
package org.springframework.web.reactive.result.method.annotation; package org.springframework.web.reactive.result.method.annotation;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.time.Duration;
import java.nio.charset.Charset; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import reactor.core.test.TestSubscriber;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Encoder; import org.springframework.core.codec.support.ByteBufferEncoder;
import org.springframework.core.codec.support.JacksonJsonEncoder; import org.springframework.core.codec.support.JacksonJsonEncoder;
import org.springframework.core.codec.support.Jaxb2Encoder;
import org.springframework.core.codec.support.StringEncoder; import org.springframework.core.codec.support.StringEncoder;
import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.io.buffer.support.DataBufferTestUtils; import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.reactive.CodecHttpMessageConverter; import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
import org.springframework.http.converter.reactive.HttpMessageConverter; import org.springframework.http.converter.reactive.HttpMessageConverter;
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
import org.springframework.http.server.reactive.MockServerHttpRequest; import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.ExtendedModelMap;
import org.springframework.web.method.HandlerMethod; import org.springframework.ui.ModelMap;
import org.springframework.util.ObjectUtils;
import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.HandlerResultHandler;
import org.springframework.web.reactive.accept.FixedContentTypeResolver;
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager; import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
/** /**
* Unit tests for {@link ResponseEntityResultHandler}. * Unit tests for {@link ResponseEntityResultHandler}. When adding a test also
* consider whether the logic under test is in a parent class, then see:
* <ul>
* <li>{@code MessageConverterResultHandlerTests},
* <li>{@code ContentNegotiatingResultHandlerSupportTests}
* </ul>
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class ResponseEntityResultHandlerTests { public class ResponseEntityResultHandlerTests {
private MockServerHttpResponse response = new MockServerHttpResponse(); private static final Object HANDLER = new Object();
@Test private ResponseEntityResultHandler resultHandler;
public void supports() throws NoSuchMethodException {
ResponseEntityResultHandler handler = createHandler(new StringEncoder());
TestController controller = new TestController();
HandlerMethod hm = new HandlerMethod(controller, TestController.class.getMethod("responseString")); private MockServerHttpResponse response = new MockServerHttpResponse();
ResolvableType type = ResolvableType.forMethodParameter(hm.getReturnType());
assertTrue(handler.supports(new HandlerResult(hm, null, type, new ExtendedModelMap())));
hm = new HandlerMethod(controller, TestController.class.getMethod("responseVoid")); private ServerWebExchange exchange;
type = ResolvableType.forMethodParameter(hm.getReturnType());
assertTrue(handler.supports(new HandlerResult(hm, null, type, new ExtendedModelMap())));
hm = new HandlerMethod(controller, TestController.class.getMethod("string"));
type = ResolvableType.forMethodParameter(hm.getReturnType());
assertFalse(handler.supports(new HandlerResult(hm, null, type, new ExtendedModelMap())));
}
@Test @Before
public void defaultOrder() throws Exception { public void setUp() throws Exception {
ResponseEntityResultHandler handler = createHandler(new StringEncoder()); this.resultHandler = createHandler();
assertEquals(0, handler.getOrder()); ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/path"));
} this.exchange = new DefaultServerWebExchange(request, this.response, mock(WebSessionManager.class));
@Test
public void jsonResponseBody() throws Exception {
RequestedContentTypeResolver resolver = new FixedContentTypeResolver(APPLICATION_JSON_UTF8);
HandlerResultHandler handler = createHandler(resolver, new StringEncoder(), new JacksonJsonEncoder());
TestController controller = new TestController();
HandlerMethod hm = new HandlerMethod(controller, controller.getClass().getMethod("responseString"));
ResolvableType type = ResolvableType.forMethodParameter(hm.getReturnType());
HandlerResult result = new HandlerResult(hm, ResponseEntity.ok("fooValue"), type);
ServerWebExchange exchange = createExchange("/foo");
handler.handleResult(exchange, result).block();
assertEquals(HttpStatus.OK, this.response.getStatus());
assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType());
TestSubscriber.subscribe(this.response.getBody())
.assertValuesWith(buf -> assertEquals("\"fooValue\"",
DataBufferTestUtils.dumpString(buf, Charset.forName("UTF-8"))));
} }
private ResponseEntityResultHandler createHandler(HttpMessageConverter<?>... converters) {
List<HttpMessageConverter<?>> converterList;
if (ObjectUtils.isEmpty(converters)) {
converterList = new ArrayList<>();
converterList.add(new CodecHttpMessageConverter<>(new ByteBufferEncoder()));
converterList.add(new CodecHttpMessageConverter<>(new StringEncoder()));
converterList.add(new ResourceHttpMessageConverter());
converterList.add(new CodecHttpMessageConverter<>(new Jaxb2Encoder()));
converterList.add(new CodecHttpMessageConverter<>(new JacksonJsonEncoder()));
}
else {
converterList = Arrays.asList(converters);
}
GenericConversionService service = new GenericConversionService();
service.addConverter(new ReactiveStreamsToCompletableFutureConverter());
service.addConverter(new ReactiveStreamsToRxJava1Converter());
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
private ResponseEntityResultHandler createHandler(Encoder<?>... encoders) { return new ResponseEntityResultHandler(converterList, new DefaultConversionService(), resolver);
return createHandler(new HeaderContentTypeResolver(), encoders);
} }
private ResponseEntityResultHandler createHandler(RequestedContentTypeResolver resolver,
Encoder<?>... encoders) {
List<HttpMessageConverter<?>> converters = Arrays.stream(encoders) @Test
.map(encoder -> new CodecHttpMessageConverter<>(encoder, null)) public void supports() throws NoSuchMethodException {
.collect(Collectors.toList()); ModelMap model = new ExtendedModelMap();
return new ResponseEntityResultHandler(converters, new DefaultConversionService(), resolver);
}
private ServerWebExchange createExchange(String path) throws URISyntaxException { ResolvableType type = ResolvableType.forClassWithGenerics(ResponseEntity.class, String.class);
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI(path)); assertTrue(this.resultHandler.supports(new HandlerResult(HANDLER, null, type, model)));
WebSessionManager sessionManager = mock(WebSessionManager.class);
return new DefaultServerWebExchange(request, this.response, sessionManager);
}
type = ResolvableType.forClassWithGenerics(ResponseEntity.class, Void.class);
assertTrue(this.resultHandler.supports(new HandlerResult(HANDLER, null, type, model)));
@SuppressWarnings("unused") type = ResolvableType.forClass(Void.class);
private static class TestController { assertFalse(this.resultHandler.supports(new HandlerResult(HANDLER, null, type, model)));
}
public ResponseEntity<String> responseString() { @Test
return null; public void defaultOrder() throws Exception {
} assertEquals(0, this.resultHandler.getOrder());
}
public ResponseEntity<Void> responseVoid() { @Test
return null; public void statusCode() throws Exception {
} ResolvableType type = ResolvableType.forClassWithGenerics(ResponseEntity.class, Void.class);
HandlerResult result = new HandlerResult(HANDLER, ResponseEntity.noContent().build(), type);
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertEquals(HttpStatus.NO_CONTENT, this.response.getStatus());
assertEquals(0, this.response.getHeaders().size());
assertNull(this.response.getBody());
}
public String string() { @Test
return null; public void headers() throws Exception {
} URI location = new URI("/path");
ResolvableType type = ResolvableType.forClassWithGenerics(ResponseEntity.class, Void.class);
HandlerResult result = new HandlerResult(HANDLER, ResponseEntity.created(location).build(), type);
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertEquals(HttpStatus.CREATED, this.response.getStatus());
assertEquals(1, this.response.getHeaders().size());
assertEquals(location, this.response.getHeaders().getLocation());
assertNull(this.response.getBody());
} }
} }

Loading…
Cancel
Save