57 changed files with 5856 additions and 118 deletions
@ -0,0 +1,220 @@
@@ -0,0 +1,220 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.reactive.server; |
||||
|
||||
import java.time.Duration; |
||||
import java.util.function.Consumer; |
||||
|
||||
import org.hamcrest.Matcher; |
||||
import org.hamcrest.MatcherAssert; |
||||
|
||||
import org.springframework.http.ResponseCookie; |
||||
import org.springframework.test.util.AssertionErrors; |
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat; |
||||
|
||||
/** |
||||
* Assertions on cookies of the response. |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class CookieAssertions { |
||||
|
||||
private final ExchangeResult exchangeResult; |
||||
|
||||
private final WebTestClient.ResponseSpec responseSpec; |
||||
|
||||
|
||||
public CookieAssertions(ExchangeResult exchangeResult, WebTestClient.ResponseSpec responseSpec) { |
||||
this.exchangeResult = exchangeResult; |
||||
this.responseSpec = responseSpec; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Expect a header with the given name to match the specified values. |
||||
*/ |
||||
public WebTestClient.ResponseSpec valueEquals(String name, String value) { |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name); |
||||
AssertionErrors.assertEquals(message, value, getCookie(name).getValue()); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert the first value of the response cookie with a Hamcrest {@link Matcher}. |
||||
*/ |
||||
public WebTestClient.ResponseSpec value(String name, Matcher<? super String> matcher) { |
||||
String value = getCookie(name).getValue(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name); |
||||
MatcherAssert.assertThat(message, value, matcher); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Consume the value of the response cookie. |
||||
*/ |
||||
public WebTestClient.ResponseSpec value(String name, Consumer<String> consumer) { |
||||
String value = getCookie(name).getValue(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> consumer.accept(value)); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Expect that the cookie with the given name is present. |
||||
*/ |
||||
public WebTestClient.ResponseSpec exists(String name) { |
||||
getCookie(name); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Expect that the cookie with the given name is not present. |
||||
*/ |
||||
public WebTestClient.ResponseSpec doesNotExist(String name) { |
||||
ResponseCookie cookie = this.exchangeResult.getResponseCookies().getFirst(name); |
||||
if (cookie != null) { |
||||
String message = getMessage(name) + " exists with value=[" + cookie.getValue() + "]"; |
||||
this.exchangeResult.assertWithDiagnostics(() -> AssertionErrors.fail(message)); |
||||
} |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert a cookie's maxAge attribute. |
||||
*/ |
||||
public WebTestClient.ResponseSpec maxAge(String name, Duration expected) { |
||||
Duration maxAge = getCookie(name).getMaxAge(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name) + " maxAge"; |
||||
AssertionErrors.assertEquals(message, expected, maxAge); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert a cookie's maxAge attribute with a Hamcrest {@link Matcher}. |
||||
*/ |
||||
public WebTestClient.ResponseSpec maxAge(String name, Matcher<? super Long> matcher) { |
||||
long maxAge = getCookie(name).getMaxAge().getSeconds(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name) + " maxAge"; |
||||
assertThat(message, maxAge, matcher); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert a cookie's path attribute. |
||||
*/ |
||||
public WebTestClient.ResponseSpec path(String name, String expected) { |
||||
String path = getCookie(name).getPath(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name) + " path"; |
||||
AssertionErrors.assertEquals(message, expected, path); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert a cookie's path attribute with a Hamcrest {@link Matcher}. |
||||
*/ |
||||
public WebTestClient.ResponseSpec path(String name, Matcher<? super String> matcher) { |
||||
String path = getCookie(name).getPath(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name) + " path"; |
||||
assertThat(message, path, matcher); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert a cookie's domain attribute. |
||||
*/ |
||||
public WebTestClient.ResponseSpec domain(String name, String expected) { |
||||
String path = getCookie(name).getDomain(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name) + " domain"; |
||||
AssertionErrors.assertEquals(message, expected, path); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert a cookie's domain attribute with a Hamcrest {@link Matcher}. |
||||
*/ |
||||
public WebTestClient.ResponseSpec domain(String name, Matcher<? super String> matcher) { |
||||
String domain = getCookie(name).getDomain(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name) + " domain"; |
||||
assertThat(message, domain, matcher); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert a cookie's secure attribute. |
||||
*/ |
||||
public WebTestClient.ResponseSpec secure(String name, boolean expected) { |
||||
boolean isSecure = getCookie(name).isSecure(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name) + " secure"; |
||||
AssertionErrors.assertEquals(message, expected, isSecure); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert a cookie's httpOnly attribute. |
||||
*/ |
||||
public WebTestClient.ResponseSpec httpOnly(String name, boolean expected) { |
||||
boolean isHttpOnly = getCookie(name).isHttpOnly(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name) + " secure"; |
||||
AssertionErrors.assertEquals(message, expected, isHttpOnly); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
/** |
||||
* Assert a cookie's sameSite attribute. |
||||
*/ |
||||
public WebTestClient.ResponseSpec sameSite(String name, String expected) { |
||||
String sameSite = getCookie(name).getSameSite(); |
||||
this.exchangeResult.assertWithDiagnostics(() -> { |
||||
String message = getMessage(name) + " secure"; |
||||
AssertionErrors.assertEquals(message, expected, sameSite); |
||||
}); |
||||
return this.responseSpec; |
||||
} |
||||
|
||||
|
||||
private ResponseCookie getCookie(String name) { |
||||
ResponseCookie cookie = this.exchangeResult.getResponseCookies().getFirst(name); |
||||
if (cookie == null) { |
||||
String message = "No cookie with name '" + name + "'"; |
||||
this.exchangeResult.assertWithDiagnostics(() -> AssertionErrors.fail(message)); |
||||
} |
||||
return cookie; |
||||
} |
||||
|
||||
private String getMessage(String cookie) { |
||||
return "Response cookie '" + cookie + "'"; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.reactive.server; |
||||
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse; |
||||
|
||||
/** |
||||
* Simple {@link ClientHttpResponse} extension that also exposes a result object |
||||
* from the underlying mock server exchange for further assertions on the state |
||||
* of the server response after the request is performed. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3 |
||||
*/ |
||||
public interface MockServerClientHttpResponse extends ClientHttpResponse { |
||||
|
||||
/** |
||||
* Return the result object with the server request and response. |
||||
*/ |
||||
Object getServerResult(); |
||||
|
||||
} |
||||
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.client; |
||||
|
||||
import javax.servlet.Filter; |
||||
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.DispatcherServletCustomizer; |
||||
import org.springframework.test.web.servlet.MockMvc; |
||||
import org.springframework.test.web.servlet.RequestBuilder; |
||||
import org.springframework.test.web.servlet.ResultMatcher; |
||||
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; |
||||
import org.springframework.test.web.servlet.setup.MockMvcConfigurer; |
||||
|
||||
/** |
||||
* Base class for implementations of {@link MockMvcTestClient.MockMvcServerSpec} |
||||
* that simply delegates to a {@link ConfigurableMockMvcBuilder} supplied by |
||||
* the concrete sub-classes. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3 |
||||
* @param <B> the type of the concrete sub-class spec |
||||
*/ |
||||
abstract class AbstractMockMvcServerSpec<B extends MockMvcTestClient.MockMvcServerSpec<B>> |
||||
implements MockMvcTestClient.MockMvcServerSpec<B> { |
||||
|
||||
@Override |
||||
public <T extends B> T filters(Filter... filters) { |
||||
getMockMvcBuilder().addFilters(filters); |
||||
return self(); |
||||
} |
||||
|
||||
public final <T extends B> T filter(Filter filter, String... urlPatterns) { |
||||
getMockMvcBuilder().addFilter(filter, urlPatterns); |
||||
return self(); |
||||
} |
||||
|
||||
@Override |
||||
public <T extends B> T defaultRequest(RequestBuilder requestBuilder) { |
||||
getMockMvcBuilder().defaultRequest(requestBuilder); |
||||
return self(); |
||||
} |
||||
|
||||
@Override |
||||
public <T extends B> T alwaysExpect(ResultMatcher resultMatcher) { |
||||
getMockMvcBuilder().alwaysExpect(resultMatcher); |
||||
return self(); |
||||
} |
||||
|
||||
@Override |
||||
public <T extends B> T dispatchOptions(boolean dispatchOptions) { |
||||
getMockMvcBuilder().dispatchOptions(dispatchOptions); |
||||
return self(); |
||||
} |
||||
|
||||
@Override |
||||
public <T extends B> T dispatcherServletCustomizer(DispatcherServletCustomizer customizer) { |
||||
getMockMvcBuilder().addDispatcherServletCustomizer(customizer); |
||||
return self(); |
||||
} |
||||
|
||||
@Override |
||||
public <T extends B> T apply(MockMvcConfigurer configurer) { |
||||
getMockMvcBuilder().apply(configurer); |
||||
return self(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private <T extends B> T self() { |
||||
return (T) this; |
||||
} |
||||
|
||||
/** |
||||
* Return the concrete {@link ConfigurableMockMvcBuilder} to delegate |
||||
* configuration methods and to use to create the {@link MockMvc}. |
||||
*/ |
||||
protected abstract ConfigurableMockMvcBuilder<?> getMockMvcBuilder(); |
||||
|
||||
@Override |
||||
public WebTestClient.Builder configureClient() { |
||||
MockMvc mockMvc = getMockMvcBuilder().build(); |
||||
ClientHttpConnector connector = new MockMvcHttpConnector(mockMvc); |
||||
return WebTestClient.bindToServer(connector); |
||||
} |
||||
|
||||
@Override |
||||
public WebTestClient build() { |
||||
return configureClient().build(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.client; |
||||
|
||||
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; |
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder; |
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
|
||||
/** |
||||
* Simple wrapper around a {@link DefaultMockMvcBuilder}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3 |
||||
*/ |
||||
class ApplicationContextMockMvcSpec extends AbstractMockMvcServerSpec<ApplicationContextMockMvcSpec> { |
||||
|
||||
private final DefaultMockMvcBuilder mockMvcBuilder; |
||||
|
||||
|
||||
public ApplicationContextMockMvcSpec(WebApplicationContext context) { |
||||
this.mockMvcBuilder = MockMvcBuilders.webAppContextSetup(context); |
||||
} |
||||
|
||||
@Override |
||||
protected ConfigurableMockMvcBuilder<?> getMockMvcBuilder() { |
||||
return this.mockMvcBuilder; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,301 @@
@@ -0,0 +1,301 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.client; |
||||
|
||||
import java.io.StringWriter; |
||||
import java.net.URI; |
||||
import java.time.Duration; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.concurrent.atomic.AtomicReference; |
||||
import java.util.function.Function; |
||||
|
||||
import javax.servlet.http.Cookie; |
||||
|
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.core.io.buffer.DataBufferUtils; |
||||
import org.springframework.core.io.buffer.DefaultDataBuffer; |
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory; |
||||
import org.springframework.http.HttpCookie; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.http.ReactiveHttpInputMessage; |
||||
import org.springframework.http.ResponseCookie; |
||||
import org.springframework.http.client.reactive.ClientHttpConnector; |
||||
import org.springframework.http.client.reactive.ClientHttpRequest; |
||||
import org.springframework.http.client.reactive.ClientHttpResponse; |
||||
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; |
||||
import org.springframework.http.codec.multipart.FilePart; |
||||
import org.springframework.http.codec.multipart.Part; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.mock.http.client.reactive.MockClientHttpRequest; |
||||
import org.springframework.mock.http.client.reactive.MockClientHttpResponse; |
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest; |
||||
import org.springframework.mock.web.MockHttpServletRequest; |
||||
import org.springframework.mock.web.MockHttpServletResponse; |
||||
import org.springframework.mock.web.MockPart; |
||||
import org.springframework.test.web.reactive.server.MockServerClientHttpResponse; |
||||
import org.springframework.test.web.servlet.MockMvc; |
||||
import org.springframework.test.web.servlet.MvcResult; |
||||
import org.springframework.test.web.servlet.RequestBuilder; |
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; |
||||
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; |
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; |
||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ObjectUtils; |
||||
import org.springframework.util.StringUtils; |
||||
import org.springframework.web.servlet.FlashMap; |
||||
import org.springframework.web.servlet.HandlerInterceptor; |
||||
import org.springframework.web.servlet.ModelAndView; |
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; |
||||
|
||||
/** |
||||
* Connector that handles requests by invoking a {@link MockMvc} rather than |
||||
* making actual requests over HTTP. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3 |
||||
*/ |
||||
public class MockMvcHttpConnector implements ClientHttpConnector { |
||||
|
||||
private static final DefaultPartHttpMessageReader MULTIPART_READER = new DefaultPartHttpMessageReader(); |
||||
|
||||
private static final Duration TIMEOUT = Duration.ofSeconds(5); |
||||
|
||||
|
||||
private final MockMvc mockMvc; |
||||
|
||||
|
||||
public MockMvcHttpConnector(MockMvc mockMvc) { |
||||
this.mockMvc = mockMvc; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Mono<ClientHttpResponse> connect( |
||||
HttpMethod method, URI uri, Function<? super ClientHttpRequest, Mono<Void>> requestCallback) { |
||||
|
||||
RequestBuilder requestBuilder = adaptRequest(method, uri, requestCallback); |
||||
try { |
||||
MvcResult mvcResult = this.mockMvc.perform(requestBuilder).andReturn(); |
||||
if (mvcResult.getRequest().isAsyncStarted()) { |
||||
mvcResult.getAsyncResult(); |
||||
mvcResult = this.mockMvc.perform(asyncDispatch(mvcResult)).andReturn(); |
||||
} |
||||
return Mono.just(adaptResponse(mvcResult)); |
||||
} |
||||
catch (Exception ex) { |
||||
return Mono.error(ex); |
||||
} |
||||
} |
||||
|
||||
private RequestBuilder adaptRequest( |
||||
HttpMethod httpMethod, URI uri, Function<? super ClientHttpRequest, Mono<Void>> requestCallback) { |
||||
|
||||
MockClientHttpRequest httpRequest = new MockClientHttpRequest(httpMethod, uri); |
||||
|
||||
AtomicReference<byte[]> contentRef = new AtomicReference<>(); |
||||
httpRequest.setWriteHandler(dataBuffers -> |
||||
DataBufferUtils.join(dataBuffers) |
||||
.doOnNext(buffer -> { |
||||
byte[] bytes = new byte[buffer.readableByteCount()]; |
||||
buffer.read(bytes); |
||||
DataBufferUtils.release(buffer); |
||||
contentRef.set(bytes); |
||||
}) |
||||
.then()); |
||||
|
||||
// Initialize the client request
|
||||
requestCallback.apply(httpRequest).block(TIMEOUT); |
||||
|
||||
MockHttpServletRequestBuilder requestBuilder = |
||||
initRequestBuilder(httpMethod, uri, httpRequest, contentRef.get()); |
||||
|
||||
requestBuilder.headers(httpRequest.getHeaders()); |
||||
for (List<HttpCookie> cookies : httpRequest.getCookies().values()) { |
||||
for (HttpCookie cookie : cookies) { |
||||
requestBuilder.cookie(new Cookie(cookie.getName(), cookie.getValue())); |
||||
} |
||||
} |
||||
|
||||
return requestBuilder; |
||||
} |
||||
|
||||
private MockHttpServletRequestBuilder initRequestBuilder( |
||||
HttpMethod httpMethod, URI uri, MockClientHttpRequest httpRequest, @Nullable byte[] bytes) { |
||||
|
||||
String contentType = httpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); |
||||
if (!StringUtils.startsWithIgnoreCase(contentType, "multipart/")) { |
||||
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.request(httpMethod, uri); |
||||
if (!ObjectUtils.isEmpty(bytes)) { |
||||
requestBuilder.content(bytes); |
||||
} |
||||
return requestBuilder; |
||||
} |
||||
|
||||
// Parse the multipart request in order to adapt to Servlet Part's
|
||||
MockMultipartHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.multipart(uri); |
||||
|
||||
Assert.notNull(bytes, "No multipart content"); |
||||
ReactiveHttpInputMessage inputMessage = MockServerHttpRequest.post(uri.toString()) |
||||
.headers(httpRequest.getHeaders()) |
||||
.body(Mono.just(DefaultDataBufferFactory.sharedInstance.wrap(bytes))); |
||||
|
||||
MULTIPART_READER.read(ResolvableType.forClass(Part.class), inputMessage, Collections.emptyMap()) |
||||
.flatMap(part -> |
||||
DataBufferUtils.join(part.content()) |
||||
.doOnNext(buffer -> { |
||||
byte[] partBytes = new byte[buffer.readableByteCount()]; |
||||
buffer.read(partBytes); |
||||
DataBufferUtils.release(buffer); |
||||
|
||||
// Adapt to javax.servlet.http.Part...
|
||||
MockPart mockPart = (part instanceof FilePart ? |
||||
new MockPart(part.name(), ((FilePart) part).filename(), partBytes) : |
||||
new MockPart(part.name(), partBytes)); |
||||
mockPart.getHeaders().putAll(part.headers()); |
||||
requestBuilder.part(mockPart); |
||||
})) |
||||
.blockLast(TIMEOUT); |
||||
|
||||
return requestBuilder; |
||||
} |
||||
|
||||
private MockClientHttpResponse adaptResponse(MvcResult mvcResult) { |
||||
MockClientHttpResponse clientResponse = new MockMvcServerClientHttpResponse(mvcResult); |
||||
MockHttpServletResponse servletResponse = mvcResult.getResponse(); |
||||
for (String header : servletResponse.getHeaderNames()) { |
||||
for (String value : servletResponse.getHeaders(header)) { |
||||
clientResponse.getHeaders().add(header, value); |
||||
} |
||||
} |
||||
if (servletResponse.getForwardedUrl() != null) { |
||||
clientResponse.getHeaders().add("Forwarded-Url", servletResponse.getForwardedUrl()); |
||||
} |
||||
for (Cookie cookie : servletResponse.getCookies()) { |
||||
ResponseCookie httpCookie = |
||||
ResponseCookie.fromClientResponse(cookie.getName(), cookie.getValue()) |
||||
.maxAge(Duration.ofSeconds(cookie.getMaxAge())) |
||||
.domain(cookie.getDomain()) |
||||
.path(cookie.getPath()) |
||||
.secure(cookie.getSecure()) |
||||
.httpOnly(cookie.isHttpOnly()) |
||||
.build(); |
||||
clientResponse.getCookies().add(httpCookie.getName(), httpCookie); |
||||
} |
||||
byte[] bytes = servletResponse.getContentAsByteArray(); |
||||
DefaultDataBuffer dataBuffer = DefaultDataBufferFactory.sharedInstance.wrap(bytes); |
||||
clientResponse.setBody(Mono.just(dataBuffer)); |
||||
return clientResponse; |
||||
} |
||||
|
||||
|
||||
private static class MockMvcServerClientHttpResponse |
||||
extends MockClientHttpResponse implements MockServerClientHttpResponse { |
||||
|
||||
private final MvcResult mvcResult; |
||||
|
||||
|
||||
public MockMvcServerClientHttpResponse(MvcResult result) { |
||||
super(result.getResponse().getStatus()); |
||||
this.mvcResult = new PrintingMvcResult(result); |
||||
} |
||||
|
||||
@Override |
||||
public Object getServerResult() { |
||||
return this.mvcResult; |
||||
} |
||||
} |
||||
|
||||
|
||||
private static class PrintingMvcResult implements MvcResult { |
||||
|
||||
private final MvcResult mvcResult; |
||||
|
||||
public PrintingMvcResult(MvcResult mvcResult) { |
||||
this.mvcResult = mvcResult; |
||||
} |
||||
|
||||
@Override |
||||
public MockHttpServletRequest getRequest() { |
||||
return this.mvcResult.getRequest(); |
||||
} |
||||
|
||||
@Override |
||||
public MockHttpServletResponse getResponse() { |
||||
return this.mvcResult.getResponse(); |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public Object getHandler() { |
||||
return this.mvcResult.getHandler(); |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public HandlerInterceptor[] getInterceptors() { |
||||
return this.mvcResult.getInterceptors(); |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public ModelAndView getModelAndView() { |
||||
return this.mvcResult.getModelAndView(); |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public Exception getResolvedException() { |
||||
return this.mvcResult.getResolvedException(); |
||||
} |
||||
|
||||
@Override |
||||
public FlashMap getFlashMap() { |
||||
return this.mvcResult.getFlashMap(); |
||||
} |
||||
|
||||
@Override |
||||
public Object getAsyncResult() { |
||||
return this.mvcResult.getAsyncResult(); |
||||
} |
||||
|
||||
@Override |
||||
public Object getAsyncResult(long timeToWait) { |
||||
return this.mvcResult.getAsyncResult(timeToWait); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
StringWriter writer = new StringWriter(); |
||||
try { |
||||
MockMvcResultHandlers.print(writer).handle(this); |
||||
} |
||||
catch (Exception ex) { |
||||
writer.append("Unable to format ") |
||||
.append(String.valueOf(this)) |
||||
.append(": ") |
||||
.append(ex.getMessage()); |
||||
} |
||||
return writer.toString(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,380 @@
@@ -0,0 +1,380 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.client; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import javax.servlet.Filter; |
||||
|
||||
import org.springframework.format.support.FormattingConversionService; |
||||
import org.springframework.http.client.reactive.ClientHttpConnector; |
||||
import org.springframework.http.converter.HttpMessageConverter; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.test.web.reactive.server.ExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.DispatcherServletCustomizer; |
||||
import org.springframework.test.web.servlet.MockMvc; |
||||
import org.springframework.test.web.servlet.MvcResult; |
||||
import org.springframework.test.web.servlet.RequestBuilder; |
||||
import org.springframework.test.web.servlet.ResultActions; |
||||
import org.springframework.test.web.servlet.ResultHandler; |
||||
import org.springframework.test.web.servlet.ResultMatcher; |
||||
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; |
||||
import org.springframework.test.web.servlet.setup.MockMvcConfigurer; |
||||
import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.validation.Validator; |
||||
import org.springframework.web.accept.ContentNegotiationManager; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; |
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler; |
||||
import org.springframework.web.servlet.FlashMapManager; |
||||
import org.springframework.web.servlet.HandlerExceptionResolver; |
||||
import org.springframework.web.servlet.HandlerInterceptor; |
||||
import org.springframework.web.servlet.LocaleResolver; |
||||
import org.springframework.web.servlet.View; |
||||
import org.springframework.web.servlet.ViewResolver; |
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; |
||||
import org.springframework.web.util.pattern.PathPatternParser; |
||||
|
||||
/** |
||||
* The main class for testing Spring MVC applications via {@link WebTestClient} |
||||
* with {@link MockMvc} for server request handling. |
||||
* |
||||
* <p>Provides static factory methods and specs to initialize {@code MockMvc} |
||||
* to which the {@code WebTestClient} connects to. For example: |
||||
* <pre class="code"> |
||||
* WebTestClient client = MockMvcTestClient.bindToController(myController) |
||||
* .controllerAdvice(myControllerAdvice) |
||||
* .validator(myValidator) |
||||
* .build() |
||||
* </pre> |
||||
* |
||||
* <p>The client itself can also be configured. For example: |
||||
* <pre class="code"> |
||||
* WebTestClient client = MockMvcTestClient.bindToController(myController) |
||||
* .validator(myValidator) |
||||
* .configureClient() |
||||
* .baseUrl("/path") |
||||
* .build(); |
||||
* </pre> |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3 |
||||
*/ |
||||
public interface MockMvcTestClient { |
||||
|
||||
/** |
||||
* Begin creating a {@link WebTestClient} by providing the {@code @Controller} |
||||
* instance(s) to handle requests with. |
||||
* <p>Internally this is delegated to and equivalent to using |
||||
* {@link org.springframework.test.web.servlet.setup.MockMvcBuilders#standaloneSetup(Object...)}. |
||||
* to initialize {@link MockMvc}. |
||||
*/ |
||||
static ControllerSpec bindToController(Object... controllers) { |
||||
return new StandaloneMockMvcSpec(controllers); |
||||
} |
||||
|
||||
/** |
||||
* Begin creating a {@link WebTestClient} by providing a |
||||
* {@link WebApplicationContext} with Spring MVC infrastructure and |
||||
* controllers. |
||||
* <p>Internally this is delegated to and equivalent to using |
||||
* {@link org.springframework.test.web.servlet.setup.MockMvcBuilders#webAppContextSetup(WebApplicationContext)} |
||||
* to initialize {@code MockMvc}. |
||||
*/ |
||||
static MockMvcServerSpec<?> bindToApplicationContext(WebApplicationContext context) { |
||||
return new ApplicationContextMockMvcSpec(context); |
||||
} |
||||
|
||||
/** |
||||
* Begin creating a {@link WebTestClient} by providing an already |
||||
* initialized {@link MockMvc} instance to use as the server. |
||||
*/ |
||||
static WebTestClient.Builder bindTo(MockMvc mockMvc) { |
||||
ClientHttpConnector connector = new MockMvcHttpConnector(mockMvc); |
||||
return WebTestClient.bindToServer(connector); |
||||
} |
||||
|
||||
/** |
||||
* This method can be used to apply further assertions on a given |
||||
* {@link ExchangeResult} based the state of the server response. |
||||
* |
||||
* <p>Normally {@link WebTestClient} is used to assert the client response |
||||
* including HTTP status, headers, and body. That is all that is available |
||||
* when making a live request over HTTP. However when the server is |
||||
* {@link MockMvc}, many more assertions are possible against the server |
||||
* response, e.g. model attributes, flash attributes, etc. |
||||
* |
||||
* <p>Example: |
||||
* <pre class="code"> |
||||
* EntityExchangeResult<Void> result = |
||||
* webTestClient.post().uri("/people/123") |
||||
* .exchange() |
||||
* .expectStatus().isFound() |
||||
* .expectHeader().location("/persons/Joe") |
||||
* .expectBody().isEmpty(); |
||||
* |
||||
* MockMvcTestClient.resultActionsFor(result) |
||||
* .andExpect(model().size(1)) |
||||
* .andExpect(model().attributeExists("name")) |
||||
* .andExpect(flash().attributeCount(1)) |
||||
* .andExpect(flash().attribute("message", "success!")); |
||||
* </pre> |
||||
* |
||||
* <p>Note: this method works only if the {@link WebTestClient} used to |
||||
* perform the request was initialized through one of bind method in this |
||||
* class, and therefore requests are handled by {@link MockMvc}. |
||||
*/ |
||||
static ResultActions resultActionsFor(ExchangeResult exchangeResult) { |
||||
Object serverResult = exchangeResult.getMockServerResult(); |
||||
Assert.notNull(serverResult, "No MvcResult"); |
||||
Assert.isInstanceOf(MvcResult.class, serverResult); |
||||
return new ResultActions() { |
||||
@Override |
||||
public ResultActions andExpect(ResultMatcher matcher) throws Exception { |
||||
matcher.match((MvcResult) serverResult); |
||||
return this; |
||||
} |
||||
@Override |
||||
public ResultActions andDo(ResultHandler handler) throws Exception { |
||||
handler.handle((MvcResult) serverResult); |
||||
return this; |
||||
} |
||||
@Override |
||||
public MvcResult andReturn() { |
||||
return (MvcResult) serverResult; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Base specification for configuring {@link MockMvc}, and a simple facade |
||||
* around {@link ConfigurableMockMvcBuilder}. |
||||
* |
||||
* @param <B> a self reference to the builder type |
||||
*/ |
||||
interface MockMvcServerSpec<B extends MockMvcServerSpec<B>> { |
||||
|
||||
/** |
||||
* Add a global filter. |
||||
* <p>This is delegated to |
||||
* {@link ConfigurableMockMvcBuilder#addFilters(Filter...)}. |
||||
*/ |
||||
<T extends B> T filters(Filter... filters); |
||||
|
||||
/** |
||||
* Add a filter for specific URL patterns. |
||||
* <p>This is delegated to |
||||
* {@link ConfigurableMockMvcBuilder#addFilter(Filter, String...)}. |
||||
*/ |
||||
<T extends B> T filter(Filter filter, String... urlPatterns); |
||||
|
||||
/** |
||||
* Define default request properties that should be merged into all |
||||
* performed requests such that input from the client request override |
||||
* the default properties defined here. |
||||
* <p>This is delegated to |
||||
* {@link ConfigurableMockMvcBuilder#defaultRequest(RequestBuilder)}. |
||||
*/ |
||||
<T extends B> T defaultRequest(RequestBuilder requestBuilder); |
||||
|
||||
/** |
||||
* Define a global expectation that should <em>always</em> be applied to |
||||
* every response. |
||||
* <p>This is delegated to |
||||
* {@link ConfigurableMockMvcBuilder#alwaysExpect(ResultMatcher)}. |
||||
*/ |
||||
<T extends B> T alwaysExpect(ResultMatcher resultMatcher); |
||||
|
||||
/** |
||||
* Whether to handle HTTP OPTIONS requests. |
||||
* <p>This is delegated to |
||||
* {@link ConfigurableMockMvcBuilder#dispatchOptions(boolean)}. |
||||
*/ |
||||
<T extends B> T dispatchOptions(boolean dispatchOptions); |
||||
|
||||
/** |
||||
* Allow customization of {@code DispatcherServlet}. |
||||
* <p>This is delegated to |
||||
* {@link ConfigurableMockMvcBuilder#addDispatcherServletCustomizer(DispatcherServletCustomizer)}. |
||||
*/ |
||||
<T extends B> T dispatcherServletCustomizer(DispatcherServletCustomizer customizer); |
||||
|
||||
/** |
||||
* Add a {@code MockMvcConfigurer} that automates MockMvc setup. |
||||
* <p>This is delegated to |
||||
* {@link ConfigurableMockMvcBuilder#apply(MockMvcConfigurer)}. |
||||
*/ |
||||
<T extends B> T apply(MockMvcConfigurer configurer); |
||||
|
||||
/** |
||||
* Proceed to configure and build the test client. |
||||
*/ |
||||
WebTestClient.Builder configureClient(); |
||||
|
||||
/** |
||||
* Shortcut to build the test client. |
||||
*/ |
||||
WebTestClient build(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Specification for configuring {@link MockMvc} to test one or more |
||||
* controllers directly, and a simple facade around |
||||
* {@link StandaloneMockMvcBuilder}. |
||||
*/ |
||||
interface ControllerSpec extends MockMvcServerSpec<ControllerSpec> { |
||||
|
||||
/** |
||||
* Register {@link org.springframework.web.bind.annotation.ControllerAdvice} |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setControllerAdvice(Object...)}. |
||||
*/ |
||||
ControllerSpec controllerAdvice(Object... controllerAdvice); |
||||
|
||||
/** |
||||
* Set the message converters to use. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setMessageConverters(HttpMessageConverter[])}. |
||||
*/ |
||||
ControllerSpec messageConverters(HttpMessageConverter<?>... messageConverters); |
||||
|
||||
/** |
||||
* Provide a custom {@link Validator}. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setValidator(Validator)}. |
||||
*/ |
||||
ControllerSpec validator(Validator validator); |
||||
|
||||
/** |
||||
* Provide a conversion service. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setConversionService(FormattingConversionService)}. |
||||
*/ |
||||
ControllerSpec conversionService(FormattingConversionService conversionService); |
||||
|
||||
/** |
||||
* Add global interceptors. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#addInterceptors(HandlerInterceptor...)}. |
||||
*/ |
||||
ControllerSpec interceptors(HandlerInterceptor... interceptors); |
||||
|
||||
/** |
||||
* Add interceptors for specific patterns. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#addMappedInterceptors(String[], HandlerInterceptor...)}. |
||||
*/ |
||||
ControllerSpec mappedInterceptors( |
||||
@Nullable String[] pathPatterns, HandlerInterceptor... interceptors); |
||||
|
||||
/** |
||||
* Set a ContentNegotiationManager. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setContentNegotiationManager(ContentNegotiationManager)}. |
||||
*/ |
||||
ControllerSpec contentNegotiationManager(ContentNegotiationManager manager); |
||||
|
||||
/** |
||||
* Specify the timeout value for async execution. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setAsyncRequestTimeout(long)}. |
||||
*/ |
||||
ControllerSpec asyncRequestTimeout(long timeout); |
||||
|
||||
/** |
||||
* Provide custom argument resolvers. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setCustomArgumentResolvers(HandlerMethodArgumentResolver...)}. |
||||
*/ |
||||
ControllerSpec customArgumentResolvers(HandlerMethodArgumentResolver... argumentResolvers); |
||||
|
||||
/** |
||||
* Provide custom return value handlers. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setCustomReturnValueHandlers(HandlerMethodReturnValueHandler...)}. |
||||
*/ |
||||
ControllerSpec customReturnValueHandlers(HandlerMethodReturnValueHandler... handlers); |
||||
|
||||
/** |
||||
* Set the HandlerExceptionResolver types to use. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setHandlerExceptionResolvers(HandlerExceptionResolver...)}. |
||||
*/ |
||||
ControllerSpec handlerExceptionResolvers(HandlerExceptionResolver... exceptionResolvers); |
||||
|
||||
/** |
||||
* Set up view resolution. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setViewResolvers(ViewResolver...)}. |
||||
*/ |
||||
ControllerSpec viewResolvers(ViewResolver... resolvers); |
||||
|
||||
/** |
||||
* Set up a single {@link ViewResolver} with a fixed view. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setSingleView(View)}. |
||||
*/ |
||||
ControllerSpec singleView(View view); |
||||
|
||||
/** |
||||
* Provide the LocaleResolver to use. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setLocaleResolver(LocaleResolver)}. |
||||
*/ |
||||
ControllerSpec localeResolver(LocaleResolver localeResolver); |
||||
|
||||
/** |
||||
* Provide a custom FlashMapManager. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setFlashMapManager(FlashMapManager)}. |
||||
*/ |
||||
ControllerSpec flashMapManager(FlashMapManager flashMapManager); |
||||
|
||||
/** |
||||
* Enable URL path matching with parsed |
||||
* {@link org.springframework.web.util.pattern.PathPattern PathPatterns}. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setPatternParser(PathPatternParser)}. |
||||
*/ |
||||
ControllerSpec patternParser(PathPatternParser parser); |
||||
|
||||
/** |
||||
* Whether to match trailing slashes. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setUseTrailingSlashPatternMatch(boolean)}. |
||||
*/ |
||||
ControllerSpec useTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch); |
||||
|
||||
/** |
||||
* Configure placeholder values to use. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#addPlaceholderValue(String, String)}. |
||||
*/ |
||||
ControllerSpec placeholderValue(String name, String value); |
||||
|
||||
/** |
||||
* Configure factory for a custom {@link RequestMappingHandlerMapping}. |
||||
* <p>This is delegated to |
||||
* {@link StandaloneMockMvcBuilder#setCustomHandlerMapping(Supplier)}. |
||||
*/ |
||||
ControllerSpec customHandlerMapping(Supplier<RequestMappingHandlerMapping> factory); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,176 @@
@@ -0,0 +1,176 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.client; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.springframework.format.support.FormattingConversionService; |
||||
import org.springframework.http.converter.HttpMessageConverter; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; |
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders; |
||||
import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder; |
||||
import org.springframework.validation.Validator; |
||||
import org.springframework.web.accept.ContentNegotiationManager; |
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; |
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler; |
||||
import org.springframework.web.servlet.FlashMapManager; |
||||
import org.springframework.web.servlet.HandlerExceptionResolver; |
||||
import org.springframework.web.servlet.HandlerInterceptor; |
||||
import org.springframework.web.servlet.LocaleResolver; |
||||
import org.springframework.web.servlet.View; |
||||
import org.springframework.web.servlet.ViewResolver; |
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; |
||||
import org.springframework.web.util.pattern.PathPatternParser; |
||||
|
||||
/** |
||||
* Simple wrapper around a {@link StandaloneMockMvcBuilder} that implements |
||||
* {@link MockMvcTestClient.ControllerSpec}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3 |
||||
*/ |
||||
class StandaloneMockMvcSpec extends AbstractMockMvcServerSpec<MockMvcTestClient.ControllerSpec> |
||||
implements MockMvcTestClient.ControllerSpec { |
||||
|
||||
private final StandaloneMockMvcBuilder mockMvcBuilder; |
||||
|
||||
|
||||
StandaloneMockMvcSpec(Object... controllers) { |
||||
this.mockMvcBuilder = MockMvcBuilders.standaloneSetup(controllers); |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec controllerAdvice(Object... controllerAdvice) { |
||||
this.mockMvcBuilder.setControllerAdvice(controllerAdvice); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec messageConverters(HttpMessageConverter<?>... messageConverters) { |
||||
this.mockMvcBuilder.setMessageConverters(messageConverters); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec validator(Validator validator) { |
||||
this.mockMvcBuilder.setValidator(validator); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec conversionService(FormattingConversionService conversionService) { |
||||
this.mockMvcBuilder.setConversionService(conversionService); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec interceptors(HandlerInterceptor... interceptors) { |
||||
mappedInterceptors(null, interceptors); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec mappedInterceptors( |
||||
@Nullable String[] pathPatterns, HandlerInterceptor... interceptors) { |
||||
|
||||
this.mockMvcBuilder.addMappedInterceptors(pathPatterns, interceptors); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec contentNegotiationManager(ContentNegotiationManager manager) { |
||||
this.mockMvcBuilder.setContentNegotiationManager(manager); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec asyncRequestTimeout(long timeout) { |
||||
this.mockMvcBuilder.setAsyncRequestTimeout(timeout); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec customArgumentResolvers(HandlerMethodArgumentResolver... argumentResolvers) { |
||||
this.mockMvcBuilder.setCustomArgumentResolvers(argumentResolvers); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec customReturnValueHandlers(HandlerMethodReturnValueHandler... handlers) { |
||||
this.mockMvcBuilder.setCustomReturnValueHandlers(handlers); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec handlerExceptionResolvers(HandlerExceptionResolver... exceptionResolvers) { |
||||
this.mockMvcBuilder.setHandlerExceptionResolvers(exceptionResolvers); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec viewResolvers(ViewResolver... resolvers) { |
||||
this.mockMvcBuilder.setViewResolvers(resolvers); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec singleView(View view) { |
||||
this.mockMvcBuilder.setSingleView(view); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec localeResolver(LocaleResolver localeResolver) { |
||||
this.mockMvcBuilder.setLocaleResolver(localeResolver); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec flashMapManager(FlashMapManager flashMapManager) { |
||||
this.mockMvcBuilder.setFlashMapManager(flashMapManager); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec patternParser(PathPatternParser parser) { |
||||
this.mockMvcBuilder.setPatternParser(parser); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec useTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch) { |
||||
this.mockMvcBuilder.setUseTrailingSlashPatternMatch(useTrailingSlashPatternMatch); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec placeholderValue(String name, String value) { |
||||
this.mockMvcBuilder.addPlaceholderValue(name, value); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public StandaloneMockMvcSpec customHandlerMapping(Supplier<RequestMappingHandlerMapping> factory) { |
||||
this.mockMvcBuilder.setCustomHandlerMapping(factory); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public ConfigurableMockMvcBuilder<?> getMockMvcBuilder() { |
||||
return this.mockMvcBuilder; |
||||
} |
||||
} |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
/** |
||||
* Support for testing Spring MVC applications via |
||||
* {@link org.springframework.test.web.reactive.server.WebTestClient} |
||||
* with {@link org.springframework.test.web.servlet.MockMvc} for server request |
||||
* handling. |
||||
*/ |
||||
|
||||
@NonNullApi |
||||
@NonNullFields |
||||
package org.springframework.test.web.servlet.client; |
||||
|
||||
import org.springframework.lang.NonNullApi; |
||||
import org.springframework.lang.NonNullFields; |
||||
@ -0,0 +1,138 @@
@@ -0,0 +1,138 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.reactive.server; |
||||
|
||||
import java.net.URI; |
||||
import java.time.Duration; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import reactor.core.publisher.MonoProcessor; |
||||
import reactor.core.publisher.Sinks; |
||||
|
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.ResponseCookie; |
||||
import org.springframework.mock.http.client.reactive.MockClientHttpRequest; |
||||
import org.springframework.mock.http.client.reactive.MockClientHttpResponse; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.mockito.Mockito.mock; |
||||
|
||||
/** |
||||
* Unit tests for {@link CookieAssertions} |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class CookieAssertionTests { |
||||
|
||||
private final ResponseCookie cookie = ResponseCookie.from("foo", "bar") |
||||
.maxAge(Duration.ofMinutes(30)) |
||||
.domain("foo.com") |
||||
.path("/foo") |
||||
.secure(true) |
||||
.httpOnly(true) |
||||
.sameSite("Lax") |
||||
.build(); |
||||
|
||||
private final CookieAssertions assertions = cookieAssertions(cookie); |
||||
|
||||
|
||||
@Test |
||||
void valueEquals() { |
||||
assertions.valueEquals("foo", "bar"); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.valueEquals("what?!", "bar")); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.valueEquals("foo", "what?!")); |
||||
} |
||||
|
||||
@Test |
||||
void value() { |
||||
assertions.value("foo", equalTo("bar")); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.value("foo", equalTo("what?!"))); |
||||
} |
||||
|
||||
@Test |
||||
void exists() { |
||||
assertions.exists("foo"); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.exists("what?!")); |
||||
} |
||||
|
||||
@Test |
||||
void doesNotExist() { |
||||
assertions.doesNotExist("what?!"); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.doesNotExist("foo")); |
||||
} |
||||
|
||||
@Test |
||||
void maxAge() { |
||||
assertions.maxAge("foo", Duration.ofMinutes(30)); |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertions.maxAge("foo", Duration.ofMinutes(29))); |
||||
|
||||
assertions.maxAge("foo", equalTo(Duration.ofMinutes(30).getSeconds())); |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertions.maxAge("foo", equalTo(Duration.ofMinutes(29).getSeconds()))); |
||||
} |
||||
|
||||
@Test |
||||
void domain() { |
||||
assertions.domain("foo", "foo.com"); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.domain("foo", "what.com")); |
||||
|
||||
assertions.domain("foo", equalTo("foo.com")); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.domain("foo", equalTo("what.com"))); |
||||
} |
||||
|
||||
@Test |
||||
void path() { |
||||
assertions.path("foo", "/foo"); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.path("foo", "/what")); |
||||
|
||||
assertions.path("foo", equalTo("/foo")); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.path("foo", equalTo("/what"))); |
||||
} |
||||
|
||||
@Test |
||||
void secure() { |
||||
assertions.secure("foo", true); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.secure("foo", false)); |
||||
} |
||||
|
||||
@Test |
||||
void httpOnly() { |
||||
assertions.httpOnly("foo", true); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.httpOnly("foo", false)); |
||||
} |
||||
|
||||
@Test |
||||
void sameSite() { |
||||
assertions.sameSite("foo", "Lax"); |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertions.sameSite("foo", "Strict")); |
||||
} |
||||
|
||||
|
||||
private CookieAssertions cookieAssertions(ResponseCookie cookie) { |
||||
MockClientHttpRequest request = new MockClientHttpRequest(HttpMethod.GET, URI.create("/")); |
||||
MockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK); |
||||
response.getCookies().add(cookie.getName(), cookie); |
||||
|
||||
MonoProcessor<byte[]> emptyContent = MonoProcessor.fromSink(Sinks.one()); |
||||
emptyContent.onComplete(); |
||||
|
||||
ExchangeResult result = new ExchangeResult(request, response, emptyContent, emptyContent, Duration.ZERO, null, null); |
||||
return new CookieAssertions(result, mock(WebTestClient.ResponseSpec.class)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,119 @@
@@ -0,0 +1,119 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.context; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
import java.util.concurrent.Callable; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
import org.mockito.Mockito; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.ContextHierarchy; |
||||
import org.springframework.test.context.junit.jupiter.SpringExtension; |
||||
import org.springframework.test.context.web.WebAppConfiguration; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
import org.springframework.web.context.request.async.CallableProcessingInterceptor; |
||||
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer; |
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc; |
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
|
||||
import static org.mockito.ArgumentMatchers.any; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.context.AsyncControllerJavaConfigTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
@ExtendWith(SpringExtension.class) |
||||
@WebAppConfiguration |
||||
@ContextHierarchy(@ContextConfiguration(classes = AsyncControllerJavaConfigTests.WebConfig.class)) |
||||
public class AsyncControllerJavaConfigTests { |
||||
|
||||
@Autowired |
||||
private WebApplicationContext wac; |
||||
|
||||
@Autowired |
||||
private CallableProcessingInterceptor callableInterceptor; |
||||
|
||||
private WebTestClient testClient; |
||||
|
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
this.testClient = MockMvcTestClient.bindToApplicationContext(this.wac).build(); |
||||
} |
||||
|
||||
@Test |
||||
public void callableInterceptor() throws Exception { |
||||
testClient.get().uri("/callable") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().json("{\"key\":\"value\"}"); |
||||
|
||||
Mockito.verify(this.callableInterceptor).beforeConcurrentHandling(any(), any()); |
||||
Mockito.verify(this.callableInterceptor).preProcess(any(), any()); |
||||
Mockito.verify(this.callableInterceptor).postProcess(any(), any(), any()); |
||||
Mockito.verify(this.callableInterceptor).afterCompletion(any(), any()); |
||||
Mockito.verifyNoMoreInteractions(this.callableInterceptor); |
||||
} |
||||
|
||||
|
||||
@Configuration |
||||
@EnableWebMvc |
||||
static class WebConfig implements WebMvcConfigurer { |
||||
|
||||
@Override |
||||
public void configureAsyncSupport(AsyncSupportConfigurer configurer) { |
||||
configurer.registerCallableInterceptors(callableInterceptor()); |
||||
} |
||||
|
||||
@Bean |
||||
public CallableProcessingInterceptor callableInterceptor() { |
||||
return Mockito.mock(CallableProcessingInterceptor.class); |
||||
} |
||||
|
||||
@Bean |
||||
public AsyncController asyncController() { |
||||
return new AsyncController(); |
||||
} |
||||
|
||||
} |
||||
|
||||
@RestController |
||||
static class AsyncController { |
||||
|
||||
@GetMapping("/callable") |
||||
public Callable<Map<String, String>> getCallable() { |
||||
return () -> Collections.singletonMap("key", "value"); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,148 @@
@@ -0,0 +1,148 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.context; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
import org.mockito.Mockito; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.ContextHierarchy; |
||||
import org.springframework.test.context.junit.jupiter.SpringExtension; |
||||
import org.springframework.test.context.web.WebAppConfiguration; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.test.web.servlet.samples.context.PersonController; |
||||
import org.springframework.test.web.servlet.samples.context.PersonDao; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; |
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc; |
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; |
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; |
||||
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; |
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
import org.springframework.web.servlet.view.tiles3.TilesConfigurer; |
||||
|
||||
import static org.mockito.BDDMockito.given; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.context.JavaConfigTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
@ExtendWith(SpringExtension.class) |
||||
@WebAppConfiguration("classpath:META-INF/web-resources") |
||||
@ContextHierarchy({ |
||||
@ContextConfiguration(classes = JavaConfigTests.RootConfig.class), |
||||
@ContextConfiguration(classes = JavaConfigTests.WebConfig.class) |
||||
}) |
||||
public class JavaConfigTests { |
||||
|
||||
@Autowired |
||||
private WebApplicationContext wac; |
||||
|
||||
@Autowired |
||||
private PersonDao personDao; |
||||
|
||||
@Autowired |
||||
private PersonController personController; |
||||
|
||||
private WebTestClient testClient; |
||||
|
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
this.testClient = MockMvcTestClient.bindToApplicationContext(this.wac).build(); |
||||
given(this.personDao.getPerson(5L)).willReturn(new Person("Joe")); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void person() { |
||||
testClient.get().uri("/person/5") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().json("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"); |
||||
} |
||||
|
||||
@Test |
||||
public void tilesDefinitions() { |
||||
testClient.get().uri("/") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals("Forwarded-Url", "/WEB-INF/layouts/standardLayout.jsp"); |
||||
} |
||||
|
||||
|
||||
@Configuration |
||||
static class RootConfig { |
||||
|
||||
@Bean |
||||
public PersonDao personDao() { |
||||
return Mockito.mock(PersonDao.class); |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
@EnableWebMvc |
||||
static class WebConfig implements WebMvcConfigurer { |
||||
|
||||
@Autowired |
||||
private RootConfig rootConfig; |
||||
|
||||
@Bean |
||||
public PersonController personController() { |
||||
return new PersonController(this.rootConfig.personDao()); |
||||
} |
||||
|
||||
@Override |
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
||||
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); |
||||
} |
||||
|
||||
@Override |
||||
public void addViewControllers(ViewControllerRegistry registry) { |
||||
registry.addViewController("/").setViewName("home"); |
||||
} |
||||
|
||||
@Override |
||||
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { |
||||
configurer.enable(); |
||||
} |
||||
|
||||
@Override |
||||
public void configureViewResolvers(ViewResolverRegistry registry) { |
||||
registry.tiles(); |
||||
} |
||||
|
||||
@Bean |
||||
public TilesConfigurer tilesConfigurer() { |
||||
TilesConfigurer configurer = new TilesConfigurer(); |
||||
configurer.setDefinitions("/WEB-INF/**/tiles.xml"); |
||||
return configurer; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.context; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.ContextHierarchy; |
||||
import org.springframework.test.context.junit.jupiter.SpringExtension; |
||||
import org.springframework.test.context.web.WebAppConfiguration; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler; |
||||
|
||||
import static org.hamcrest.Matchers.containsString; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.handler; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.context.WebAppResourceTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
@ExtendWith(SpringExtension.class) |
||||
@WebAppConfiguration("src/test/resources/META-INF/web-resources") |
||||
@ContextHierarchy({ |
||||
@ContextConfiguration("../../context/root-context.xml"), |
||||
@ContextConfiguration("../../context/servlet-context.xml") |
||||
}) |
||||
public class WebAppResourceTests { |
||||
|
||||
@Autowired |
||||
private WebApplicationContext wac; |
||||
|
||||
private WebTestClient testClient; |
||||
|
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
this.testClient = MockMvcTestClient.bindToApplicationContext(this.wac).build(); |
||||
} |
||||
|
||||
// TilesConfigurer: resources under "/WEB-INF/**/tiles.xml"
|
||||
|
||||
@Test |
||||
public void tilesDefinitions() { |
||||
testClient.get().uri("/") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals("Forwarded-Url", "/WEB-INF/layouts/standardLayout.jsp"); |
||||
} |
||||
|
||||
// Resources served via <mvc:resources/>
|
||||
|
||||
@Test |
||||
public void resourceRequest() { |
||||
testClient.get().uri("/resources/Spring.js") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType("application/javascript") |
||||
.expectBody(String.class).value(containsString("Spring={};")); |
||||
} |
||||
|
||||
// Forwarded to the "default" servlet via <mvc:default-servlet-handler/>
|
||||
|
||||
@Test |
||||
public void resourcesViaDefaultServlet() throws Exception { |
||||
EntityExchangeResult<Void> result = testClient.get().uri("/unknown/resource") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().isEmpty(); |
||||
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andExpect(handler().handlerType(DefaultServletHttpRequestHandler.class)) |
||||
.andExpect(forwardedUrl("default")); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.context; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.ContextHierarchy; |
||||
import org.springframework.test.context.junit.jupiter.SpringExtension; |
||||
import org.springframework.test.context.web.WebAppConfiguration; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.test.web.servlet.samples.context.PersonDao; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
|
||||
import static org.mockito.BDDMockito.given; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.context.XmlConfigTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
@ExtendWith(SpringExtension.class) |
||||
@WebAppConfiguration("src/test/resources/META-INF/web-resources") |
||||
@ContextHierarchy({ |
||||
@ContextConfiguration("../../context/root-context.xml"), |
||||
@ContextConfiguration("../../context/servlet-context.xml") |
||||
}) |
||||
public class XmlConfigTests { |
||||
|
||||
@Autowired |
||||
private WebApplicationContext wac; |
||||
|
||||
@Autowired |
||||
private PersonDao personDao; |
||||
|
||||
private WebTestClient testClient; |
||||
|
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
this.testClient = MockMvcTestClient.bindToApplicationContext(this.wac).build(); |
||||
given(this.personDao.getPerson(5L)).willReturn(new Person("Joe")); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void person() { |
||||
testClient.get().uri("/person/5") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().json("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"); |
||||
} |
||||
|
||||
@Test |
||||
public void tilesDefinitions() { |
||||
testClient.get().uri("/") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals("Forwarded-Url", "/WEB-INF/layouts/standardLayout.jsp"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,221 @@
@@ -0,0 +1,221 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
import java.time.Duration; |
||||
import java.util.concurrent.Callable; |
||||
import java.util.concurrent.CompletableFuture; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.util.concurrent.ListenableFuture; |
||||
import org.springframework.util.concurrent.ListenableFutureTask; |
||||
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.ResponseStatus; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.context.request.async.DeferredResult; |
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.AsyncTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class AsyncTests { |
||||
|
||||
private final WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new AsyncController()).build(); |
||||
|
||||
|
||||
@Test |
||||
public void callable() { |
||||
this.testClient.get() |
||||
.uri("/1?callable=true") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON) |
||||
.expectBody().json("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"); |
||||
} |
||||
|
||||
@Test |
||||
public void streaming() { |
||||
this.testClient.get() |
||||
.uri("/1?streaming=true") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).isEqualTo("name=Joe"); |
||||
} |
||||
|
||||
@Test |
||||
public void streamingSlow() { |
||||
this.testClient.get() |
||||
.uri("/1?streamingSlow=true") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).isEqualTo("name=Joe&someBoolean=true"); |
||||
} |
||||
|
||||
@Test |
||||
public void streamingJson() { |
||||
this.testClient.get() |
||||
.uri("/1?streamingJson=true") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON) |
||||
.expectBody().json("{\"name\":\"Joe\",\"someDouble\":0.5}"); |
||||
} |
||||
|
||||
@Test |
||||
public void deferredResult() { |
||||
this.testClient.get() |
||||
.uri("/1?deferredResult=true") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON) |
||||
.expectBody().json("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"); |
||||
} |
||||
|
||||
@Test |
||||
public void deferredResultWithImmediateValue() throws Exception { |
||||
this.testClient.get() |
||||
.uri("/1?deferredResultWithImmediateValue=true") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON) |
||||
.expectBody().json("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"); |
||||
} |
||||
|
||||
@Test |
||||
public void deferredResultWithDelayedError() throws Exception { |
||||
this.testClient.get() |
||||
.uri("/1?deferredResultWithDelayedError=true") |
||||
.exchange() |
||||
.expectStatus().is5xxServerError() |
||||
.expectBody(String.class).isEqualTo("Delayed Error"); |
||||
} |
||||
|
||||
@Test |
||||
public void listenableFuture() throws Exception { |
||||
this.testClient.get() |
||||
.uri("/1?listenableFuture=true") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON) |
||||
.expectBody().json("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"); |
||||
} |
||||
|
||||
@Test |
||||
public void completableFutureWithImmediateValue() throws Exception { |
||||
this.testClient.get() |
||||
.uri("/1?completableFutureWithImmediateValue=true") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON) |
||||
.expectBody().json("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"); |
||||
} |
||||
|
||||
|
||||
@RestController |
||||
@RequestMapping(path = "/{id}", produces = "application/json") |
||||
private static class AsyncController { |
||||
|
||||
@RequestMapping(params = "callable") |
||||
public Callable<Person> getCallable() { |
||||
return () -> new Person("Joe"); |
||||
} |
||||
|
||||
@RequestMapping(params = "streaming") |
||||
public StreamingResponseBody getStreaming() { |
||||
return os -> os.write("name=Joe".getBytes(StandardCharsets.UTF_8)); |
||||
} |
||||
|
||||
@RequestMapping(params = "streamingSlow") |
||||
public StreamingResponseBody getStreamingSlow() { |
||||
return os -> { |
||||
os.write("name=Joe".getBytes()); |
||||
try { |
||||
Thread.sleep(200); |
||||
os.write("&someBoolean=true".getBytes(StandardCharsets.UTF_8)); |
||||
} |
||||
catch (InterruptedException e) { |
||||
/* no-op */ |
||||
} |
||||
}; |
||||
} |
||||
|
||||
@RequestMapping(params = "streamingJson") |
||||
public ResponseEntity<StreamingResponseBody> getStreamingJson() { |
||||
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON) |
||||
.body(os -> os.write("{\"name\":\"Joe\",\"someDouble\":0.5}".getBytes(StandardCharsets.UTF_8))); |
||||
} |
||||
|
||||
@RequestMapping(params = "deferredResult") |
||||
public DeferredResult<Person> getDeferredResult() { |
||||
DeferredResult<Person> result = new DeferredResult<>(); |
||||
delay(100, () -> result.setResult(new Person("Joe"))); |
||||
return result; |
||||
} |
||||
|
||||
@RequestMapping(params = "deferredResultWithImmediateValue") |
||||
public DeferredResult<Person> getDeferredResultWithImmediateValue() { |
||||
DeferredResult<Person> result = new DeferredResult<>(); |
||||
result.setResult(new Person("Joe")); |
||||
return result; |
||||
} |
||||
|
||||
@RequestMapping(params = "deferredResultWithDelayedError") |
||||
public DeferredResult<Person> getDeferredResultWithDelayedError() { |
||||
DeferredResult<Person> result = new DeferredResult<>(); |
||||
delay(100, () -> result.setErrorResult(new RuntimeException("Delayed Error"))); |
||||
return result; |
||||
} |
||||
|
||||
@RequestMapping(params = "listenableFuture") |
||||
public ListenableFuture<Person> getListenableFuture() { |
||||
ListenableFutureTask<Person> futureTask = new ListenableFutureTask<>(() -> new Person("Joe")); |
||||
delay(100, futureTask); |
||||
return futureTask; |
||||
} |
||||
|
||||
@RequestMapping(params = "completableFutureWithImmediateValue") |
||||
public CompletableFuture<Person> getCompletableFutureWithImmediateValue() { |
||||
CompletableFuture<Person> future = new CompletableFuture<>(); |
||||
future.complete(new Person("Joe")); |
||||
return future; |
||||
} |
||||
|
||||
@ExceptionHandler(Exception.class) |
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) |
||||
public String errorHandler(Exception ex) { |
||||
return ex.getMessage(); |
||||
} |
||||
|
||||
private void delay(long millis, Runnable task) { |
||||
Mono.delay(Duration.ofMillis(millis)).doOnTerminate(task).subscribe(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,238 @@
@@ -0,0 +1,238 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import org.junit.jupiter.api.Nested; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.core.Ordered; |
||||
import org.springframework.core.annotation.Order; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.ControllerAdvice; |
||||
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.bind.annotation.RestControllerAdvice; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.ExceptionHandlerTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
class ExceptionHandlerTests { |
||||
|
||||
@Nested |
||||
class MvcTests { |
||||
|
||||
@Test |
||||
void localExceptionHandlerMethod() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()).build(); |
||||
|
||||
client.get().uri("/person/Clyde") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals("Forwarded-Url", "errorView"); |
||||
} |
||||
|
||||
@Test |
||||
void globalExceptionHandlerMethod() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()) |
||||
.controllerAdvice(new GlobalExceptionHandler()) |
||||
.build(); |
||||
|
||||
client.get().uri("/person/Bonnie") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals("Forwarded-Url", "globalErrorView"); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class PersonController { |
||||
|
||||
@GetMapping("/person/{name}") |
||||
String show(@PathVariable String name) { |
||||
if (name.equals("Clyde")) { |
||||
throw new IllegalArgumentException("simulated exception"); |
||||
} |
||||
else if (name.equals("Bonnie")) { |
||||
throw new IllegalStateException("simulated exception"); |
||||
} |
||||
return "person/show"; |
||||
} |
||||
|
||||
@ExceptionHandler |
||||
String handleException(IllegalArgumentException exception) { |
||||
return "errorView"; |
||||
} |
||||
} |
||||
|
||||
@ControllerAdvice |
||||
private static class GlobalExceptionHandler { |
||||
|
||||
@ExceptionHandler |
||||
String handleException(IllegalStateException exception) { |
||||
return "globalErrorView"; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Nested |
||||
class RestTests { |
||||
|
||||
@Test |
||||
void noException() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new RestPersonController()) |
||||
.controllerAdvice(new RestPersonControllerExceptionHandler()) |
||||
.build(); |
||||
|
||||
client.get().uri("/person/Yoda") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().jsonPath("$.name", "Yoda"); |
||||
} |
||||
|
||||
@Test |
||||
void localExceptionHandlerMethod() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new RestPersonController()) |
||||
.controllerAdvice(new RestPersonControllerExceptionHandler()) |
||||
.build(); |
||||
|
||||
client.get().uri("/person/Luke") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().jsonPath("$.error", "local - IllegalArgumentException"); |
||||
} |
||||
|
||||
@Test |
||||
void globalExceptionHandlerMethod() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new RestPersonController()) |
||||
.controllerAdvice(new RestGlobalExceptionHandler()) |
||||
.build(); |
||||
|
||||
client.get().uri("/person/Leia") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().jsonPath("$.error", "global - IllegalArgumentException"); |
||||
} |
||||
|
||||
@Test |
||||
void globalRestPersonControllerExceptionHandlerTakesPrecedenceOverGlobalExceptionHandler() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new RestPersonController()) |
||||
.controllerAdvice(RestGlobalExceptionHandler.class, RestPersonControllerExceptionHandler.class) |
||||
.build(); |
||||
|
||||
client.get().uri("/person/Leia") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().jsonPath("$.error", "globalPersonController - IllegalStateException"); |
||||
} |
||||
|
||||
@Test |
||||
void noHandlerFound() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new RestPersonController()) |
||||
.controllerAdvice(RestGlobalExceptionHandler.class, RestPersonControllerExceptionHandler.class) |
||||
.dispatcherServletCustomizer(servlet -> servlet.setThrowExceptionIfNoHandlerFound(true)) |
||||
.build(); |
||||
|
||||
client.get().uri("/bogus") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().jsonPath("$.error", "global - NoHandlerFoundException"); |
||||
} |
||||
} |
||||
|
||||
|
||||
@RestController |
||||
private static class RestPersonController { |
||||
|
||||
@GetMapping("/person/{name}") |
||||
Person get(@PathVariable String name) { |
||||
switch (name) { |
||||
case "Luke": |
||||
throw new IllegalArgumentException(); |
||||
case "Leia": |
||||
throw new IllegalStateException(); |
||||
default: |
||||
return new Person("Yoda"); |
||||
} |
||||
} |
||||
|
||||
@ExceptionHandler |
||||
Error handleException(IllegalArgumentException exception) { |
||||
return new Error("local - " + exception.getClass().getSimpleName()); |
||||
} |
||||
} |
||||
|
||||
@RestControllerAdvice(assignableTypes = RestPersonController.class) |
||||
@Order(Ordered.HIGHEST_PRECEDENCE) |
||||
private static class RestPersonControllerExceptionHandler { |
||||
|
||||
@ExceptionHandler |
||||
Error handleException(Throwable exception) { |
||||
return new Error("globalPersonController - " + exception.getClass().getSimpleName()); |
||||
} |
||||
} |
||||
|
||||
@RestControllerAdvice |
||||
@Order(Ordered.LOWEST_PRECEDENCE) |
||||
private static class RestGlobalExceptionHandler { |
||||
|
||||
@ExceptionHandler |
||||
Error handleException(Throwable exception) { |
||||
return new Error( "global - " + exception.getClass().getSimpleName()); |
||||
} |
||||
} |
||||
|
||||
static class Person { |
||||
|
||||
private final String name; |
||||
|
||||
Person(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
} |
||||
|
||||
static class Error { |
||||
|
||||
private final String error; |
||||
|
||||
Error(String error) { |
||||
this.error = error; |
||||
} |
||||
|
||||
public String getError() { |
||||
return error; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,323 @@
@@ -0,0 +1,323 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import java.io.IOException; |
||||
import java.security.Principal; |
||||
import java.util.concurrent.CompletableFuture; |
||||
|
||||
import javax.servlet.AsyncContext; |
||||
import javax.servlet.AsyncListener; |
||||
import javax.servlet.FilterChain; |
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.ServletException; |
||||
import javax.servlet.ServletRequest; |
||||
import javax.servlet.ServletResponse; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletRequestWrapper; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import javax.servlet.http.HttpServletResponseWrapper; |
||||
import javax.validation.Valid; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.validation.Errors; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
import org.springframework.web.filter.OncePerRequestFilter; |
||||
import org.springframework.web.filter.ShallowEtagHeaderFilter; |
||||
import org.springframework.web.servlet.ModelAndView; |
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes; |
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.FilterTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class FilterTests { |
||||
|
||||
@Test |
||||
public void whenFiltersCompleteMvcProcessesRequest() throws Exception { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()) |
||||
.filters(new ContinueFilter()) |
||||
.build(); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = client.post().uri("/persons?name=Andy") |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectHeader().location("/person/1") |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().size(1)) |
||||
.andExpect(model().attributeExists("id")) |
||||
.andExpect(flash().attributeCount(1)) |
||||
.andExpect(flash().attribute("message", "success!")); |
||||
} |
||||
|
||||
@Test |
||||
public void filtersProcessRequest() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()) |
||||
.filters(new ContinueFilter(), new RedirectFilter()) |
||||
.build(); |
||||
|
||||
client.post().uri("/persons?name=Andy") |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectHeader().location("/login"); |
||||
} |
||||
|
||||
@Test |
||||
public void filterMappedBySuffix() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()) |
||||
.filter(new RedirectFilter(), "*.html") |
||||
.build(); |
||||
|
||||
client.post().uri("/persons.html?name=Andy") |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectHeader().location("/login"); |
||||
} |
||||
|
||||
@Test |
||||
public void filterWithExactMapping() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()) |
||||
.filter(new RedirectFilter(), "/p", "/persons") |
||||
.build(); |
||||
|
||||
client.post().uri("/persons?name=Andy") |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectHeader().location("/login"); |
||||
} |
||||
|
||||
@Test |
||||
public void filterSkipped() throws Exception { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()) |
||||
.filter(new RedirectFilter(), "/p", "/person") |
||||
.build(); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = |
||||
client.post().uri("/persons?name=Andy") |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectHeader().location("/person/1") |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().size(1)) |
||||
.andExpect(model().attributeExists("id")) |
||||
.andExpect(flash().attributeCount(1)) |
||||
.andExpect(flash().attribute("message", "success!")); |
||||
} |
||||
|
||||
@Test |
||||
public void filterWrapsRequestResponse() throws Exception { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()) |
||||
.filter(new WrappingRequestResponseFilter()) |
||||
.build(); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = |
||||
client.post().uri("/user").exchange().expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attribute("principal", WrappingRequestResponseFilter.PRINCIPAL_NAME)); |
||||
} |
||||
|
||||
@Test |
||||
public void filterWrapsRequestResponseAndPerformsAsyncDispatch() { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()) |
||||
.filters(new WrappingRequestResponseFilter(), new ShallowEtagHeaderFilter()) |
||||
.build(); |
||||
|
||||
client.get().uri("/persons/1") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentLength(53) |
||||
.expectHeader().valueEquals("ETag", "\"0e37becb4f0c90709cb2e1efcc61eaa00\"") |
||||
.expectBody().json("{\"name\":\"Lukas\",\"someDouble\":0.0,\"someBoolean\":false}"); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class PersonController { |
||||
|
||||
@PostMapping(path="/persons") |
||||
public String save(@Valid Person person, Errors errors, RedirectAttributes redirectAttrs) { |
||||
if (errors.hasErrors()) { |
||||
return "person/add"; |
||||
} |
||||
redirectAttrs.addAttribute("id", "1"); |
||||
redirectAttrs.addFlashAttribute("message", "success!"); |
||||
return "redirect:/person/{id}"; |
||||
} |
||||
|
||||
@PostMapping("/user") |
||||
public ModelAndView user(Principal principal) { |
||||
return new ModelAndView("user/view", "principal", principal.getName()); |
||||
} |
||||
|
||||
@GetMapping("/forward") |
||||
public String forward() { |
||||
return "forward:/persons"; |
||||
} |
||||
|
||||
@GetMapping("persons/{id}") |
||||
@ResponseBody |
||||
public CompletableFuture<Person> getPerson() { |
||||
return CompletableFuture.completedFuture(new Person("Lukas")); |
||||
} |
||||
} |
||||
|
||||
private class ContinueFilter extends OncePerRequestFilter { |
||||
|
||||
@Override |
||||
protected void doFilterInternal(HttpServletRequest request, |
||||
HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { |
||||
|
||||
filterChain.doFilter(request, response); |
||||
} |
||||
} |
||||
|
||||
private static class WrappingRequestResponseFilter extends OncePerRequestFilter { |
||||
|
||||
public static final String PRINCIPAL_NAME = "WrapRequestResponseFilterPrincipal"; |
||||
|
||||
|
||||
@Override |
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, |
||||
FilterChain filterChain) throws ServletException, IOException { |
||||
|
||||
filterChain.doFilter(new HttpServletRequestWrapper(request) { |
||||
|
||||
@Override |
||||
public Principal getUserPrincipal() { |
||||
return () -> PRINCIPAL_NAME; |
||||
} |
||||
|
||||
// Like Spring Security does in HttpServlet3RequestFactory..
|
||||
|
||||
@Override |
||||
public AsyncContext getAsyncContext() { |
||||
return super.getAsyncContext() != null ? |
||||
new AsyncContextWrapper(super.getAsyncContext()) : null; |
||||
} |
||||
|
||||
}, new HttpServletResponseWrapper(response)); |
||||
} |
||||
} |
||||
|
||||
private class RedirectFilter extends OncePerRequestFilter { |
||||
|
||||
@Override |
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, |
||||
FilterChain filterChain) throws ServletException, IOException { |
||||
|
||||
response.sendRedirect("/login"); |
||||
} |
||||
} |
||||
|
||||
|
||||
private static class AsyncContextWrapper implements AsyncContext { |
||||
|
||||
private final AsyncContext delegate; |
||||
|
||||
public AsyncContextWrapper(AsyncContext delegate) { |
||||
this.delegate = delegate; |
||||
} |
||||
|
||||
@Override |
||||
public ServletRequest getRequest() { |
||||
return this.delegate.getRequest(); |
||||
} |
||||
|
||||
@Override |
||||
public ServletResponse getResponse() { |
||||
return this.delegate.getResponse(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean hasOriginalRequestAndResponse() { |
||||
return this.delegate.hasOriginalRequestAndResponse(); |
||||
} |
||||
|
||||
@Override |
||||
public void dispatch() { |
||||
this.delegate.dispatch(); |
||||
} |
||||
|
||||
@Override |
||||
public void dispatch(String path) { |
||||
this.delegate.dispatch(path); |
||||
} |
||||
|
||||
@Override |
||||
public void dispatch(ServletContext context, String path) { |
||||
this.delegate.dispatch(context, path); |
||||
} |
||||
|
||||
@Override |
||||
public void complete() { |
||||
this.delegate.complete(); |
||||
} |
||||
|
||||
@Override |
||||
public void start(Runnable run) { |
||||
this.delegate.start(run); |
||||
} |
||||
|
||||
@Override |
||||
public void addListener(AsyncListener listener) { |
||||
this.delegate.addListener(listener); |
||||
} |
||||
|
||||
@Override |
||||
public void addListener(AsyncListener listener, ServletRequest req, ServletResponse res) { |
||||
this.delegate.addListener(listener, req, res); |
||||
} |
||||
|
||||
@Override |
||||
public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException { |
||||
return this.delegate.createListener(clazz); |
||||
} |
||||
|
||||
@Override |
||||
public void setTimeout(long timeout) { |
||||
this.delegate.setTimeout(timeout); |
||||
} |
||||
|
||||
@Override |
||||
public long getTimeout() { |
||||
return this.delegate.getTimeout(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,112 @@
@@ -0,0 +1,112 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import java.security.Principal; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.test.web.servlet.request.RequestPostProcessor; |
||||
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; |
||||
import org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
|
||||
import static org.mockito.Mockito.mock; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.FrameworkExtensionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class FrameworkExtensionTests { |
||||
|
||||
private final WebTestClient client = |
||||
MockMvcTestClient.bindToController(new SampleController()) |
||||
.apply(defaultSetup()) |
||||
.build(); |
||||
|
||||
|
||||
@Test |
||||
public void fooHeader() { |
||||
this.client.get().uri("/") |
||||
.header("Foo", "a=b") |
||||
.exchange() |
||||
.expectBody(String.class).isEqualTo("Foo"); |
||||
} |
||||
|
||||
@Test |
||||
public void barHeader() { |
||||
this.client.get().uri("/") |
||||
.header("Bar", "a=b") |
||||
.exchange() |
||||
.expectBody(String.class).isEqualTo("Bar"); |
||||
} |
||||
|
||||
private static TestMockMvcConfigurer defaultSetup() { |
||||
return new TestMockMvcConfigurer(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Test {@code MockMvcConfigurer}. |
||||
*/ |
||||
private static class TestMockMvcConfigurer extends MockMvcConfigurerAdapter { |
||||
|
||||
@Override |
||||
public void afterConfigurerAdded(ConfigurableMockMvcBuilder<?> builder) { |
||||
builder.alwaysExpect(status().isOk()); |
||||
} |
||||
|
||||
@Override |
||||
public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder, |
||||
WebApplicationContext context) { |
||||
return request -> { |
||||
request.setUserPrincipal(mock(Principal.class)); |
||||
return request; |
||||
}; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
@RequestMapping("/") |
||||
private static class SampleController { |
||||
|
||||
@RequestMapping(headers = "Foo") |
||||
@ResponseBody |
||||
public String handleFoo(Principal principal) { |
||||
Assert.notNull(principal, "Principal must not be null"); |
||||
return "Foo"; |
||||
} |
||||
|
||||
@RequestMapping(headers = "Bar") |
||||
@ResponseBody |
||||
public String handleBar(Principal principal) { |
||||
Assert.notNull(principal, "Principal must not be null"); |
||||
return "Bar"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,405 @@
@@ -0,0 +1,405 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
|
||||
import javax.servlet.FilterChain; |
||||
import javax.servlet.ServletException; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletRequestWrapper; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import javax.servlet.http.Part; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.client.MultipartBodyBuilder; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.ui.Model; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestMethod; |
||||
import org.springframework.web.bind.annotation.RequestParam; |
||||
import org.springframework.web.bind.annotation.RequestPart; |
||||
import org.springframework.web.filter.OncePerRequestFilter; |
||||
import org.springframework.web.multipart.MultipartFile; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.MultipartControllerTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class MultipartControllerTests { |
||||
|
||||
private final WebTestClient testClient = MockMvcTestClient.bindToController(new MultipartController()).build(); |
||||
|
||||
|
||||
@Test |
||||
public void multipartRequestWithSingleFile() throws Exception { |
||||
|
||||
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("file", fileContent).filename("orig"); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = testClient.post().uri("/multipartfile") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attribute("fileContent", fileContent)) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithSingleFileNotPresent() { |
||||
testClient.post().uri("/multipartfile") |
||||
.exchange() |
||||
.expectStatus().isFound(); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithFileArray() throws Exception { |
||||
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("file", fileContent).filename("orig"); |
||||
bodyBuilder.part("file", fileContent).filename("orig"); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = testClient.post().uri("/multipartfilearray") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attribute("fileContent", fileContent)) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithFileArrayNoMultipart() { |
||||
testClient.post().uri("/multipartfilearray") |
||||
.exchange() |
||||
.expectStatus().isFound(); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithOptionalFile() throws Exception { |
||||
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("file", fileContent).filename("orig"); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = testClient.post().uri("/optionalfile") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attribute("fileContent", fileContent)) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithOptionalFileNotPresent() throws Exception { |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = testClient.post().uri("/optionalfile") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attributeDoesNotExist("fileContent")) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithOptionalFileArray() throws Exception { |
||||
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("file", fileContent).filename("orig"); |
||||
bodyBuilder.part("file", fileContent).filename("orig"); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = testClient.post().uri("/optionalfilearray") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attribute("fileContent", fileContent)) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithOptionalFileArrayNotPresent() throws Exception { |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = testClient.post().uri("/optionalfilearray") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attributeDoesNotExist("fileContent")) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithOptionalFileList() throws Exception { |
||||
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("file", fileContent).filename("orig"); |
||||
bodyBuilder.part("file", fileContent).filename("orig"); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = testClient.post().uri("/optionalfilelist") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attribute("fileContent", fileContent)) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithOptionalFileListNotPresent() throws Exception { |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = testClient.post().uri("/optionalfilelist") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attributeDoesNotExist("fileContent")) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWithServletParts() throws Exception { |
||||
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("file", fileContent).filename("orig"); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = testClient.post().uri("/multipartfile") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attribute("fileContent", fileContent)) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
@Test |
||||
public void multipartRequestWrapped() throws Exception { |
||||
Map<String, String> json = Collections.singletonMap("name", "yeeeah"); |
||||
|
||||
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); |
||||
bodyBuilder.part("json", json, MediaType.APPLICATION_JSON); |
||||
|
||||
WebTestClient client = MockMvcTestClient.bindToController(new MultipartController()) |
||||
.filter(new RequestWrappingFilter()) |
||||
.build(); |
||||
|
||||
EntityExchangeResult<Void> exchangeResult = client.post().uri("/multipartfile") |
||||
.bodyValue(bodyBuilder.build()) |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().attribute("jsonContent", json)); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class MultipartController { |
||||
|
||||
@RequestMapping(value = "/multipartfile", method = RequestMethod.POST) |
||||
public String processMultipartFile(@RequestParam(required = false) MultipartFile file, |
||||
@RequestPart(required = false) Map<String, String> json, Model model) throws IOException { |
||||
|
||||
if (file != null) { |
||||
model.addAttribute("fileContent", file.getBytes()); |
||||
} |
||||
if (json != null) { |
||||
model.addAttribute("jsonContent", json); |
||||
} |
||||
|
||||
return "redirect:/index"; |
||||
} |
||||
|
||||
@RequestMapping(value = "/multipartfilearray", method = RequestMethod.POST) |
||||
public String processMultipartFileArray(@RequestParam(required = false) MultipartFile[] file, |
||||
@RequestPart(required = false) Map<String, String> json, Model model) throws IOException { |
||||
|
||||
if (file != null && file.length > 0) { |
||||
byte[] content = file[0].getBytes(); |
||||
assertThat(file[1].getBytes()).isEqualTo(content); |
||||
model.addAttribute("fileContent", content); |
||||
} |
||||
if (json != null) { |
||||
model.addAttribute("jsonContent", json); |
||||
} |
||||
|
||||
return "redirect:/index"; |
||||
} |
||||
|
||||
@RequestMapping(value = "/multipartfilelist", method = RequestMethod.POST) |
||||
public String processMultipartFileList(@RequestParam(required = false) List<MultipartFile> file, |
||||
@RequestPart(required = false) Map<String, String> json, Model model) throws IOException { |
||||
|
||||
if (file != null && !file.isEmpty()) { |
||||
byte[] content = file.get(0).getBytes(); |
||||
assertThat(file.get(1).getBytes()).isEqualTo(content); |
||||
model.addAttribute("fileContent", content); |
||||
} |
||||
if (json != null) { |
||||
model.addAttribute("jsonContent", json); |
||||
} |
||||
|
||||
return "redirect:/index"; |
||||
} |
||||
|
||||
@RequestMapping(value = "/optionalfile", method = RequestMethod.POST) |
||||
public String processOptionalFile(@RequestParam Optional<MultipartFile> file, |
||||
@RequestPart Map<String, String> json, Model model) throws IOException { |
||||
|
||||
if (file.isPresent()) { |
||||
model.addAttribute("fileContent", file.get().getBytes()); |
||||
} |
||||
model.addAttribute("jsonContent", json); |
||||
|
||||
return "redirect:/index"; |
||||
} |
||||
|
||||
@RequestMapping(value = "/optionalfilearray", method = RequestMethod.POST) |
||||
public String processOptionalFileArray(@RequestParam Optional<MultipartFile[]> file, |
||||
@RequestPart Map<String, String> json, Model model) throws IOException { |
||||
|
||||
if (file.isPresent()) { |
||||
byte[] content = file.get()[0].getBytes(); |
||||
assertThat(file.get()[1].getBytes()).isEqualTo(content); |
||||
model.addAttribute("fileContent", content); |
||||
} |
||||
model.addAttribute("jsonContent", json); |
||||
|
||||
return "redirect:/index"; |
||||
} |
||||
|
||||
@RequestMapping(value = "/optionalfilelist", method = RequestMethod.POST) |
||||
public String processOptionalFileList(@RequestParam Optional<List<MultipartFile>> file, |
||||
@RequestPart Map<String, String> json, Model model) throws IOException { |
||||
|
||||
if (file.isPresent()) { |
||||
byte[] content = file.get().get(0).getBytes(); |
||||
assertThat(file.get().get(1).getBytes()).isEqualTo(content); |
||||
model.addAttribute("fileContent", content); |
||||
} |
||||
model.addAttribute("jsonContent", json); |
||||
|
||||
return "redirect:/index"; |
||||
} |
||||
|
||||
@RequestMapping(value = "/part", method = RequestMethod.POST) |
||||
public String processPart(@RequestParam Part part, |
||||
@RequestPart Map<String, String> json, Model model) throws IOException { |
||||
|
||||
model.addAttribute("fileContent", part.getInputStream()); |
||||
model.addAttribute("jsonContent", json); |
||||
|
||||
return "redirect:/index"; |
||||
} |
||||
|
||||
@RequestMapping(value = "/json", method = RequestMethod.POST) |
||||
public String processMultipart(@RequestPart Map<String, String> json, Model model) { |
||||
model.addAttribute("json", json); |
||||
return "redirect:/index"; |
||||
} |
||||
} |
||||
|
||||
|
||||
private static class RequestWrappingFilter extends OncePerRequestFilter { |
||||
|
||||
@Override |
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, |
||||
FilterChain filterChain) throws IOException, ServletException { |
||||
|
||||
request = new HttpServletRequestWrapper(request); |
||||
filterChain.doFilter(request, response); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import java.time.Duration; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.test.StepVerifier; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.ReactiveReturnTypeTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ReactiveReturnTypeTests { |
||||
|
||||
@Test |
||||
public void sseWithFlux() { |
||||
|
||||
WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new ReactiveController()).build(); |
||||
|
||||
Flux<String> bodyFlux = testClient.get().uri("/spr16869") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentTypeCompatibleWith(TEXT_EVENT_STREAM) |
||||
.returnResult(String.class) |
||||
.getResponseBody(); |
||||
|
||||
StepVerifier.create(bodyFlux) |
||||
.expectNext("event0") |
||||
.expectNext("event1") |
||||
.expectNext("event2") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
|
||||
@RestController |
||||
static class ReactiveController { |
||||
|
||||
@GetMapping(path = "/spr16869", produces = MediaType.TEXT_EVENT_STREAM_VALUE) |
||||
Flux<String> sseFlux() { |
||||
return Flux.interval(Duration.ofSeconds(1)).take(3) |
||||
.map(aLong -> String.format("event%d", aLong)); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,163 @@
@@ -0,0 +1,163 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import javax.validation.Valid; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers; |
||||
import org.springframework.ui.Model; |
||||
import org.springframework.validation.Errors; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes; |
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.RedirectTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class RedirectTests { |
||||
|
||||
private final WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new PersonController()).build(); |
||||
|
||||
|
||||
@Test |
||||
public void save() throws Exception { |
||||
EntityExchangeResult<Void> exchangeResult = |
||||
testClient.post().uri("/persons?name=Andy") |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectHeader().location("/persons/Joe") |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(exchangeResult) |
||||
.andExpect(model().size(1)) |
||||
.andExpect(model().attributeExists("name")) |
||||
.andExpect(flash().attributeCount(1)) |
||||
.andExpect(flash().attribute("message", "success!")); |
||||
} |
||||
|
||||
@Test |
||||
public void saveSpecial() throws Exception { |
||||
EntityExchangeResult<Void> result = |
||||
testClient.post().uri("/people?name=Andy") |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectHeader().location("/persons/Joe") |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andExpect(model().size(1)) |
||||
.andExpect(model().attributeExists("name")) |
||||
.andExpect(flash().attributeCount(1)) |
||||
.andExpect(flash().attribute("message", "success!")); |
||||
} |
||||
|
||||
@Test |
||||
public void saveWithErrors() throws Exception { |
||||
EntityExchangeResult<Void> result = |
||||
testClient.post().uri("/persons").exchange().expectStatus().isOk().expectBody().isEmpty(); |
||||
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andExpect(forwardedUrl("persons/add")) |
||||
.andExpect(model().size(1)) |
||||
.andExpect(model().attributeExists("person")) |
||||
.andExpect(flash().attributeCount(0)); |
||||
} |
||||
|
||||
@Test |
||||
public void saveSpecialWithErrors() throws Exception { |
||||
EntityExchangeResult<Void> result = |
||||
testClient.post().uri("/people").exchange().expectStatus().isOk().expectBody().isEmpty(); |
||||
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andExpect(forwardedUrl("persons/add")) |
||||
.andExpect(model().size(1)) |
||||
.andExpect(model().attributeExists("person")) |
||||
.andExpect(flash().attributeCount(0)); |
||||
} |
||||
|
||||
@Test |
||||
public void getPerson() throws Exception { |
||||
EntityExchangeResult<Void> result = |
||||
MockMvcTestClient.bindToController(new PersonController()) |
||||
.defaultRequest(get("/").flashAttr("message", "success!")) |
||||
.build() |
||||
.get().uri("/persons/Joe") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andDo(MockMvcResultHandlers.print()) |
||||
.andExpect(forwardedUrl("persons/index")) |
||||
.andExpect(model().size(2)) |
||||
.andExpect(model().attribute("person", new Person("Joe"))) |
||||
.andExpect(model().attribute("message", "success!")) |
||||
.andExpect(flash().attributeCount(0)); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class PersonController { |
||||
|
||||
@GetMapping("/persons/{name}") |
||||
public String getPerson(@PathVariable String name, Model model) { |
||||
model.addAttribute(new Person(name)); |
||||
return "persons/index"; |
||||
} |
||||
|
||||
@PostMapping("/persons") |
||||
public String save(@Valid Person person, Errors errors, RedirectAttributes redirectAttrs) { |
||||
if (errors.hasErrors()) { |
||||
return "persons/add"; |
||||
} |
||||
redirectAttrs.addAttribute("name", "Joe"); |
||||
redirectAttrs.addFlashAttribute("message", "success!"); |
||||
return "redirect:/persons/{name}"; |
||||
} |
||||
|
||||
@PostMapping("/people") |
||||
public Object saveSpecial(@Valid Person person, Errors errors, RedirectAttributes redirectAttrs) { |
||||
if (errors.hasErrors()) { |
||||
return "persons/add"; |
||||
} |
||||
redirectAttrs.addAttribute("name", "Joe"); |
||||
redirectAttrs.addFlashAttribute("message", "success!"); |
||||
return new StringBuilder("redirect:").append("/persons").append("/{name}"); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestParam; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.RequestParameterTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class RequestParameterTests { |
||||
|
||||
@Test |
||||
public void queryParameter() { |
||||
|
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()).build(); |
||||
|
||||
client.get().uri("/search?name=George") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON) |
||||
.expectBody().jsonPath("$.name", "George"); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private class PersonController { |
||||
|
||||
@RequestMapping(value="/search") |
||||
@ResponseBody |
||||
public Person get(@RequestParam String name) { |
||||
Person person = new Person(name); |
||||
return person; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import javax.validation.constraints.NotNull; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import static org.hamcrest.Matchers.equalTo; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.ResponseBodyTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ResponseBodyTests { |
||||
|
||||
@Test |
||||
void json() { |
||||
MockMvcTestClient.bindToController(new PersonController()).build() |
||||
.get() |
||||
.uri("/person/Lee") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON) |
||||
.expectBody() |
||||
.jsonPath("$.name").isEqualTo("Lee") |
||||
.jsonPath("$.age").isEqualTo(42) |
||||
.jsonPath("$.age").value(equalTo(42)) |
||||
.jsonPath("$.age").value(equalTo(42.0f), Float.class); |
||||
} |
||||
|
||||
|
||||
@RestController |
||||
private static class PersonController { |
||||
|
||||
@GetMapping("/person/{name}") |
||||
public Person get(@PathVariable String name) { |
||||
Person person = new Person(name); |
||||
person.setAge(42); |
||||
return person; |
||||
} |
||||
} |
||||
|
||||
private static class Person { |
||||
|
||||
@NotNull |
||||
private final String name; |
||||
|
||||
private int age; |
||||
|
||||
public Person(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public int getAge() { |
||||
return this.age; |
||||
} |
||||
|
||||
public void setAge(int age) { |
||||
this.age = age; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,183 @@
@@ -0,0 +1,183 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.oxm.jaxb.Jaxb2Marshaller; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.ui.Model; |
||||
import org.springframework.web.accept.ContentNegotiationManager; |
||||
import org.springframework.web.accept.FixedContentNegotiationStrategy; |
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.servlet.View; |
||||
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; |
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver; |
||||
import org.springframework.web.servlet.view.json.MappingJackson2JsonView; |
||||
import org.springframework.web.servlet.view.xml.MarshallingView; |
||||
|
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.hasProperty; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.RequestParameterTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
class ViewResolutionTests { |
||||
|
||||
@Test |
||||
void jspOnly() throws Exception { |
||||
WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new PersonController()) |
||||
.viewResolvers(new InternalResourceViewResolver("/WEB-INF/", ".jsp")) |
||||
.build(); |
||||
|
||||
EntityExchangeResult<Void> result = testClient.get().uri("/person/Corea") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andExpect(status().isOk()) |
||||
.andExpect(model().size(1)) |
||||
.andExpect(model().attributeExists("person")) |
||||
.andExpect(forwardedUrl("/WEB-INF/person/show.jsp")); |
||||
} |
||||
|
||||
@Test |
||||
void jsonOnly() { |
||||
WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new PersonController()) |
||||
.singleView(new MappingJackson2JsonView()) |
||||
.build(); |
||||
|
||||
testClient.get().uri("/person/Corea") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentTypeCompatibleWith(MediaType.APPLICATION_JSON) |
||||
.expectBody().jsonPath("$.person.name", "Corea"); |
||||
} |
||||
|
||||
@Test |
||||
void xmlOnly() { |
||||
Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); |
||||
marshaller.setClassesToBeBound(Person.class); |
||||
|
||||
WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new PersonController()) |
||||
.singleView(new MarshallingView(marshaller)) |
||||
.build(); |
||||
|
||||
testClient.get().uri("/person/Corea") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_XML) |
||||
.expectBody().xpath("/person/name/text()").isEqualTo("Corea"); |
||||
} |
||||
|
||||
@Test |
||||
void contentNegotiation() throws Exception { |
||||
Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); |
||||
marshaller.setClassesToBeBound(Person.class); |
||||
|
||||
List<View> viewList = new ArrayList<>(); |
||||
viewList.add(new MappingJackson2JsonView()); |
||||
viewList.add(new MarshallingView(marshaller)); |
||||
|
||||
ContentNegotiationManager manager = new ContentNegotiationManager( |
||||
new HeaderContentNegotiationStrategy(), new FixedContentNegotiationStrategy(MediaType.TEXT_HTML)); |
||||
|
||||
ContentNegotiatingViewResolver cnViewResolver = new ContentNegotiatingViewResolver(); |
||||
cnViewResolver.setDefaultViews(viewList); |
||||
cnViewResolver.setContentNegotiationManager(manager); |
||||
cnViewResolver.afterPropertiesSet(); |
||||
|
||||
WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new PersonController()) |
||||
.viewResolvers(cnViewResolver, new InternalResourceViewResolver()) |
||||
.build(); |
||||
|
||||
EntityExchangeResult<Void> result = testClient.get().uri("/person/Corea") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andExpect(model().size(1)) |
||||
.andExpect(model().attributeExists("person")) |
||||
.andExpect(forwardedUrl("person/show")); |
||||
|
||||
testClient.get().uri("/person/Corea") |
||||
.accept(MediaType.APPLICATION_JSON) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentTypeCompatibleWith(MediaType.APPLICATION_JSON) |
||||
.expectBody().jsonPath("$.person.name", "Corea"); |
||||
|
||||
testClient.get().uri("/person/Corea") |
||||
.accept(MediaType.APPLICATION_XML) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_XML) |
||||
.expectBody().xpath("/person/name/text()").isEqualTo("Corea"); |
||||
} |
||||
|
||||
@Test |
||||
void defaultViewResolver() throws Exception { |
||||
WebTestClient client = MockMvcTestClient.bindToController(new PersonController()).build(); |
||||
|
||||
EntityExchangeResult<Void> result = client.get().uri("/person/Corea") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().isEmpty(); |
||||
|
||||
// Further assertions on the server response
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andExpect(model().attribute("person", hasProperty("name", equalTo("Corea")))) |
||||
.andExpect(forwardedUrl("person/show")); // InternalResourceViewResolver
|
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class PersonController { |
||||
|
||||
@GetMapping("/person/{name}") |
||||
String show(@PathVariable String name, Model model) { |
||||
Person person = new Person(name); |
||||
model.addAttribute(person); |
||||
return "person/show"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resulthandlers; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
|
||||
import javax.servlet.http.Cookie; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
import org.junit.jupiter.api.Disabled; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resulthandlers.PrintingResultHandlerSmokeTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
@Disabled |
||||
public class PrintingResultHandlerSmokeTests { |
||||
|
||||
private final WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new SimpleController()).build(); |
||||
|
||||
|
||||
// Not intended to be executed with the build.
|
||||
// Comment out class-level @Disabled to see the output.
|
||||
|
||||
@Test |
||||
public void printViaConsumer() { |
||||
testClient.post().uri("/") |
||||
.contentType(MediaType.TEXT_PLAIN) |
||||
.bodyValue("Hello Request".getBytes(StandardCharsets.UTF_8)) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class) |
||||
.consumeWith(System.out::println); |
||||
} |
||||
|
||||
@Test |
||||
public void returnResultAndPrint() { |
||||
EntityExchangeResult<String> result = testClient.post().uri("/") |
||||
.contentType(MediaType.TEXT_PLAIN) |
||||
.bodyValue("Hello Request".getBytes(StandardCharsets.UTF_8)) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class) |
||||
.returnResult(); |
||||
|
||||
System.out.println(result); |
||||
} |
||||
|
||||
|
||||
@RestController |
||||
private static class SimpleController { |
||||
|
||||
@PostMapping("/") |
||||
public String hello(HttpServletResponse response) { |
||||
response.addCookie(new Cookie("enigma", "42")); |
||||
return "Hello Response"; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,145 @@
@@ -0,0 +1,145 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
||||
import static org.hamcrest.Matchers.containsString; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.ContentAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ContentAssertionTests { |
||||
|
||||
private final WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new SimpleController()).build(); |
||||
|
||||
@Test |
||||
public void testContentType() { |
||||
testClient.get().uri("/handle").accept(MediaType.TEXT_PLAIN) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.valueOf("text/plain;charset=ISO-8859-1")) |
||||
.expectHeader().contentType("text/plain;charset=ISO-8859-1") |
||||
.expectHeader().contentTypeCompatibleWith("text/plain") |
||||
.expectHeader().contentTypeCompatibleWith(MediaType.TEXT_PLAIN); |
||||
|
||||
testClient.get().uri("/handleUtf8") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.valueOf("text/plain;charset=UTF-8")) |
||||
.expectHeader().contentType("text/plain;charset=UTF-8") |
||||
.expectHeader().contentTypeCompatibleWith("text/plain") |
||||
.expectHeader().contentTypeCompatibleWith(MediaType.TEXT_PLAIN); |
||||
} |
||||
|
||||
@Test |
||||
public void testContentAsString() { |
||||
|
||||
testClient.get().uri("/handle").accept(MediaType.TEXT_PLAIN) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).isEqualTo("Hello world!"); |
||||
|
||||
testClient.get().uri("/handleUtf8").accept(MediaType.TEXT_PLAIN) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).isEqualTo("\u3053\u3093\u306b\u3061\u306f\u4e16\u754c\uff01"); |
||||
|
||||
// Hamcrest matchers...
|
||||
testClient.get().uri("/handle").accept(MediaType.TEXT_PLAIN) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).value(equalTo("Hello world!")); |
||||
testClient.get().uri("/handleUtf8") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).value(equalTo("\u3053\u3093\u306b\u3061\u306f\u4e16\u754c\uff01")); |
||||
} |
||||
|
||||
@Test |
||||
public void testContentAsBytes() { |
||||
|
||||
testClient.get().uri("/handle").accept(MediaType.TEXT_PLAIN) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(byte[].class).isEqualTo( |
||||
"Hello world!".getBytes(StandardCharsets.ISO_8859_1)); |
||||
|
||||
testClient.get().uri("/handleUtf8") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(byte[].class).isEqualTo( |
||||
"\u3053\u3093\u306b\u3061\u306f\u4e16\u754c\uff01".getBytes(StandardCharsets.UTF_8)); |
||||
} |
||||
|
||||
@Test |
||||
public void testContentStringMatcher() { |
||||
testClient.get().uri("/handle").accept(MediaType.TEXT_PLAIN) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).value(containsString("world")); |
||||
} |
||||
|
||||
@Test |
||||
public void testCharacterEncoding() { |
||||
|
||||
testClient.get().uri("/handle").accept(MediaType.TEXT_PLAIN) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType("text/plain;charset=ISO-8859-1") |
||||
.expectBody(String.class).value(containsString("world")); |
||||
|
||||
testClient.get().uri("/handleUtf8") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType("text/plain;charset=UTF-8") |
||||
.expectBody(byte[].class) |
||||
.isEqualTo("\u3053\u3093\u306b\u3061\u306f\u4e16\u754c\uff01".getBytes(StandardCharsets.UTF_8)); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class SimpleController { |
||||
|
||||
@RequestMapping(value="/handle", produces="text/plain") |
||||
@ResponseBody |
||||
public String handle() { |
||||
return "Hello world!"; |
||||
} |
||||
|
||||
@RequestMapping(value="/handleUtf8", produces="text/plain;charset=UTF-8") |
||||
@ResponseBody |
||||
public String handleWithCharset() { |
||||
return "\u3053\u3093\u306b\u3061\u306f\u4e16\u754c\uff01"; // "Hello world! (Japanese)
|
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.time.Duration; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.servlet.i18n.CookieLocaleResolver; |
||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; |
||||
|
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.startsWith; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.CookieAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class CookieAssertionTests { |
||||
|
||||
private static final String COOKIE_NAME = CookieLocaleResolver.DEFAULT_COOKIE_NAME; |
||||
|
||||
private WebTestClient client; |
||||
|
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
CookieLocaleResolver localeResolver = new CookieLocaleResolver(); |
||||
localeResolver.setCookieDomain("domain"); |
||||
localeResolver.setCookieHttpOnly(true); |
||||
|
||||
client = MockMvcTestClient.bindToController(new SimpleController()) |
||||
.interceptors(new LocaleChangeInterceptor()) |
||||
.localeResolver(localeResolver) |
||||
.alwaysExpect(status().isOk()) |
||||
.configureClient() |
||||
.baseUrl("/?locale=en_US") |
||||
.build(); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void testExists() { |
||||
client.get().uri("/").exchange().expectCookie().exists(COOKIE_NAME); |
||||
} |
||||
|
||||
@Test |
||||
public void testNotExists() { |
||||
client.get().uri("/").exchange().expectCookie().doesNotExist("unknownCookie"); |
||||
} |
||||
|
||||
@Test |
||||
public void testEqualTo() { |
||||
client.get().uri("/").exchange().expectCookie().valueEquals(COOKIE_NAME, "en-US"); |
||||
client.get().uri("/").exchange().expectCookie().value(COOKIE_NAME, equalTo("en-US")); |
||||
} |
||||
|
||||
@Test |
||||
public void testMatcher() { |
||||
client.get().uri("/").exchange().expectCookie().value(COOKIE_NAME, startsWith("en-US")); |
||||
} |
||||
|
||||
@Test |
||||
public void testMaxAge() { |
||||
client.get().uri("/").exchange().expectCookie().maxAge(COOKIE_NAME, Duration.ofSeconds(-1)); |
||||
} |
||||
|
||||
@Test |
||||
public void testDomain() { |
||||
client.get().uri("/").exchange().expectCookie().domain(COOKIE_NAME, "domain"); |
||||
} |
||||
|
||||
@Test |
||||
public void testPath() { |
||||
client.get().uri("/").exchange().expectCookie().path(COOKIE_NAME, "/"); |
||||
} |
||||
|
||||
@Test |
||||
public void testSecured() { |
||||
client.get().uri("/").exchange().expectCookie().secure(COOKIE_NAME, false); |
||||
} |
||||
|
||||
@Test |
||||
public void testHttpOnly() { |
||||
client.get().uri("/").exchange().expectCookie().httpOnly(COOKIE_NAME, true); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class SimpleController { |
||||
|
||||
@RequestMapping("/") |
||||
public String home() { |
||||
return "home"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,103 @@
@@ -0,0 +1,103 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.net.URL; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.ResultActions; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
import static org.hamcrest.Matchers.closeTo; |
||||
import static org.hamcrest.Matchers.containsString; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.notNullValue; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.FlashAttributeAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class FlashAttributeAssertionTests { |
||||
|
||||
private final WebTestClient client = |
||||
MockMvcTestClient.bindToController(new PersonController()) |
||||
.alwaysExpect(status().isFound()) |
||||
.alwaysExpect(flash().attributeCount(3)) |
||||
.build(); |
||||
|
||||
|
||||
@Test |
||||
void attributeCountWithWrongCount() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> performRequest().andExpect(flash().attributeCount(1))) |
||||
.withMessage("FlashMap size expected:<1> but was:<3>"); |
||||
} |
||||
|
||||
@Test |
||||
void attributeExists() throws Exception { |
||||
performRequest().andExpect(flash().attributeExists("one", "two", "three")); |
||||
} |
||||
|
||||
@Test |
||||
void attributeEqualTo() throws Exception { |
||||
performRequest() |
||||
.andExpect(flash().attribute("one", "1")) |
||||
.andExpect(flash().attribute("two", 2.222)) |
||||
.andExpect(flash().attribute("three", new URL("https://example.com"))); |
||||
} |
||||
|
||||
@Test |
||||
void attributeMatchers() throws Exception { |
||||
performRequest() |
||||
.andExpect(flash().attribute("one", containsString("1"))) |
||||
.andExpect(flash().attribute("two", closeTo(2, 0.5))) |
||||
.andExpect(flash().attribute("three", notNullValue())) |
||||
.andExpect(flash().attribute("one", equalTo("1"))) |
||||
.andExpect(flash().attribute("two", equalTo(2.222))) |
||||
.andExpect(flash().attribute("three", equalTo(new URL("https://example.com")))); |
||||
} |
||||
|
||||
private ResultActions performRequest() { |
||||
EntityExchangeResult<Void> result = client.post().uri("/persons").exchange().expectBody().isEmpty(); |
||||
return MockMvcTestClient.resultActionsFor(result); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class PersonController { |
||||
|
||||
@PostMapping("/persons") |
||||
String save(RedirectAttributes redirectAttrs) throws Exception { |
||||
redirectAttrs.addFlashAttribute("one", "1"); |
||||
redirectAttrs.addFlashAttribute("two", 2.222); |
||||
redirectAttrs.addFlashAttribute("three", new URL("https://example.com")); |
||||
return "redirect:/person/1"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,106 @@
@@ -0,0 +1,106 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.ResultActions; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.is; |
||||
import static org.hamcrest.Matchers.not; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.handler; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.on; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.HandlerAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class HandlerAssertionTests { |
||||
|
||||
private final WebTestClient client = |
||||
MockMvcTestClient.bindToController(new SimpleController()) |
||||
.alwaysExpect(status().isOk()) |
||||
.build(); |
||||
|
||||
|
||||
@Test |
||||
public void handlerType() throws Exception { |
||||
performRequest().andExpect(handler().handlerType(SimpleController.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void methodCallOnNonMock() { |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> performRequest().andExpect(handler().methodCall("bogus"))) |
||||
.withMessageContaining("The supplied object [bogus] is not an instance of") |
||||
.withMessageContaining(MvcUriComponentsBuilder.MethodInvocationInfo.class.getName()) |
||||
.withMessageContaining("Ensure that you invoke the handler method via MvcUriComponentsBuilder.on()"); |
||||
} |
||||
|
||||
@Test |
||||
public void methodCall() throws Exception { |
||||
performRequest().andExpect(handler().methodCall(on(SimpleController.class).handle())); |
||||
} |
||||
|
||||
@Test |
||||
public void methodName() throws Exception { |
||||
performRequest().andExpect(handler().methodName("handle")); |
||||
} |
||||
|
||||
@Test |
||||
public void methodNameMatchers() throws Exception { |
||||
performRequest() |
||||
.andExpect(handler().methodName(equalTo("handle"))) |
||||
.andExpect(handler().methodName(is(not("save")))); |
||||
} |
||||
|
||||
@Test |
||||
public void method() throws Exception { |
||||
Method method = SimpleController.class.getMethod("handle"); |
||||
performRequest().andExpect(handler().method(method)); |
||||
} |
||||
|
||||
private ResultActions performRequest() { |
||||
EntityExchangeResult<Void> result = client.get().uri("/").exchange().expectBody().isEmpty(); |
||||
return MockMvcTestClient.resultActionsFor(result); |
||||
} |
||||
|
||||
|
||||
@RestController |
||||
static class SimpleController { |
||||
|
||||
@RequestMapping("/") |
||||
public ResponseEntity<Void> handle() { |
||||
return ResponseEntity.ok().build(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,272 @@
@@ -0,0 +1,272 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Date; |
||||
import java.util.Locale; |
||||
import java.util.TimeZone; |
||||
import java.util.function.Consumer; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.context.request.WebRequest; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
import static org.assertj.core.api.Assertions.fail; |
||||
import static org.hamcrest.Matchers.containsString; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.hasItems; |
||||
import static org.hamcrest.Matchers.nullValue; |
||||
import static org.hamcrest.Matchers.startsWith; |
||||
import static org.springframework.http.HttpHeaders.IF_MODIFIED_SINCE; |
||||
import static org.springframework.http.HttpHeaders.LAST_MODIFIED; |
||||
import static org.springframework.http.HttpHeaders.VARY; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.HeaderAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class HeaderAssertionTests { |
||||
|
||||
private static final String ERROR_MESSAGE = "Should have thrown an AssertionError"; |
||||
|
||||
|
||||
private String now; |
||||
|
||||
private String minuteAgo; |
||||
|
||||
private WebTestClient testClient; |
||||
|
||||
private final long currentTime = System.currentTimeMillis(); |
||||
|
||||
private SimpleDateFormat dateFormat; |
||||
|
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
this.dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); |
||||
this.dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); |
||||
this.now = dateFormat.format(new Date(this.currentTime)); |
||||
this.minuteAgo = dateFormat.format(new Date(this.currentTime - (1000 * 60))); |
||||
|
||||
PersonController controller = new PersonController(); |
||||
controller.setStubTimestamp(this.currentTime); |
||||
this.testClient = MockMvcTestClient.bindToController(controller).build(); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void stringWithCorrectResponseHeaderValue() { |
||||
testClient.get().uri("/persons/1").header(IF_MODIFIED_SINCE, minuteAgo) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals(LAST_MODIFIED, now); |
||||
} |
||||
|
||||
@Test |
||||
public void stringWithMatcherAndCorrectResponseHeaderValue() { |
||||
testClient.get().uri("/persons/1").header(IF_MODIFIED_SINCE, minuteAgo) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().value(LAST_MODIFIED, equalTo(now)); |
||||
} |
||||
|
||||
@Test |
||||
public void multiStringHeaderValue() { |
||||
testClient.get().uri("/persons/1") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals(VARY, "foo", "bar"); |
||||
} |
||||
|
||||
@Test |
||||
public void multiStringHeaderValueWithMatchers() { |
||||
testClient.get().uri("/persons/1") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().values(VARY, hasItems(containsString("foo"), startsWith("bar"))); |
||||
} |
||||
|
||||
@Test |
||||
public void dateValueWithCorrectResponseHeaderValue() { |
||||
testClient.get().uri("/persons/1") |
||||
.header(IF_MODIFIED_SINCE, minuteAgo) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEqualsDate(LAST_MODIFIED, this.currentTime); |
||||
} |
||||
|
||||
@Test |
||||
public void longValueWithCorrectResponseHeaderValue() { |
||||
testClient.get().uri("/persons/1") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals("X-Rate-Limiting", 42); |
||||
} |
||||
|
||||
@Test |
||||
public void stringWithMissingResponseHeader() { |
||||
testClient.get().uri("/persons/1") |
||||
.header(IF_MODIFIED_SINCE, now) |
||||
.exchange() |
||||
.expectStatus().isNotModified() |
||||
.expectHeader().valueEquals("X-Custom-Header"); |
||||
} |
||||
|
||||
@Test |
||||
public void stringWithMatcherAndMissingResponseHeader() { |
||||
testClient.get().uri("/persons/1").header(IF_MODIFIED_SINCE, now) |
||||
.exchange() |
||||
.expectStatus().isNotModified() |
||||
.expectHeader().value("X-Custom-Header", nullValue()); |
||||
} |
||||
|
||||
@Test |
||||
public void longValueWithMissingResponseHeader() { |
||||
try { |
||||
testClient.get().uri("/persons/1").header(IF_MODIFIED_SINCE, now) |
||||
.exchange() |
||||
.expectStatus().isNotModified() |
||||
.expectHeader().valueEquals("X-Custom-Header", 99L); |
||||
|
||||
fail(ERROR_MESSAGE); |
||||
} |
||||
catch (AssertionError err) { |
||||
if (ERROR_MESSAGE.equals(err.getMessage())) { |
||||
throw err; |
||||
} |
||||
assertThat(err.getMessage()).startsWith("Response does not contain header 'X-Custom-Header'"); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void exists() { |
||||
testClient.get().uri("/persons/1") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().exists(LAST_MODIFIED); |
||||
} |
||||
|
||||
@Test |
||||
public void existsFail() { |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> |
||||
testClient.get().uri("/persons/1") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().exists("X-Custom-Header")); |
||||
} |
||||
|
||||
@Test |
||||
public void doesNotExist() { |
||||
testClient.get().uri("/persons/1") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().doesNotExist("X-Custom-Header"); |
||||
} |
||||
|
||||
@Test |
||||
public void doesNotExistFail() { |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> |
||||
testClient.get().uri("/persons/1") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().doesNotExist(LAST_MODIFIED)); |
||||
} |
||||
|
||||
@Test |
||||
public void longValueWithIncorrectResponseHeaderValue() { |
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> |
||||
testClient.get().uri("/persons/1") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals("X-Rate-Limiting", 1)); |
||||
} |
||||
|
||||
@Test |
||||
public void stringWithMatcherAndIncorrectResponseHeaderValue() { |
||||
long secondLater = this.currentTime + 1000; |
||||
String expected = this.dateFormat.format(new Date(secondLater)); |
||||
assertIncorrectResponseHeader(spec -> spec.expectHeader().valueEquals(LAST_MODIFIED, expected), expected); |
||||
assertIncorrectResponseHeader(spec -> spec.expectHeader().value(LAST_MODIFIED, equalTo(expected)), expected); |
||||
// Comparison by date uses HttpHeaders to format the date in the error message.
|
||||
HttpHeaders headers = new HttpHeaders(); |
||||
headers.setDate("expected", secondLater); |
||||
assertIncorrectResponseHeader(spec -> spec.expectHeader().valueEqualsDate(LAST_MODIFIED, secondLater), expected); |
||||
} |
||||
|
||||
private void assertIncorrectResponseHeader(Consumer<WebTestClient.ResponseSpec> assertions, String expected) { |
||||
try { |
||||
WebTestClient.ResponseSpec spec = testClient.get().uri("/persons/1") |
||||
.header(IF_MODIFIED_SINCE, minuteAgo) |
||||
.exchange() |
||||
.expectStatus().isOk(); |
||||
|
||||
assertions.accept(spec); |
||||
|
||||
fail(ERROR_MESSAGE); |
||||
} |
||||
catch (AssertionError err) { |
||||
if (ERROR_MESSAGE.equals(err.getMessage())) { |
||||
throw err; |
||||
} |
||||
assertMessageContains(err, "Response header '" + LAST_MODIFIED + "'"); |
||||
assertMessageContains(err, expected); |
||||
assertMessageContains(err, this.now); |
||||
} |
||||
} |
||||
|
||||
private void assertMessageContains(AssertionError error, String expected) { |
||||
assertThat(error.getMessage().contains(expected)) |
||||
.as("Failure message should contain [" + expected + "], actual is [" + error.getMessage() + "]") |
||||
.isTrue(); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class PersonController { |
||||
|
||||
private long timestamp; |
||||
|
||||
public void setStubTimestamp(long timestamp) { |
||||
this.timestamp = timestamp; |
||||
} |
||||
|
||||
@RequestMapping("/persons/{id}") |
||||
public ResponseEntity<Person> showEntity(@PathVariable long id, WebRequest request) { |
||||
return ResponseEntity |
||||
.ok() |
||||
.lastModified(this.timestamp) |
||||
.header("X-Rate-Limiting", "42") |
||||
.header("Vary", "foo", "bar") |
||||
.body(new Person("Jason")); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,152 @@
@@ -0,0 +1,152 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.util.LinkedMultiValueMap; |
||||
import org.springframework.util.MultiValueMap; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import static org.hamcrest.Matchers.containsString; |
||||
import static org.hamcrest.Matchers.endsWith; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.in; |
||||
import static org.hamcrest.Matchers.is; |
||||
import static org.hamcrest.Matchers.startsWith; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.JsonPathAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class JsonPathAssertionTests { |
||||
|
||||
private final WebTestClient client = |
||||
MockMvcTestClient.bindToController(new MusicController()) |
||||
.alwaysExpect(status().isOk()) |
||||
.alwaysExpect(content().contentType(MediaType.APPLICATION_JSON)) |
||||
.configureClient() |
||||
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) |
||||
.build(); |
||||
|
||||
|
||||
@Test |
||||
public void exists() { |
||||
String composerByName = "$.composers[?(@.name == '%s')]"; |
||||
String performerByName = "$.performers[?(@.name == '%s')]"; |
||||
|
||||
client.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.jsonPath(composerByName, "Johann Sebastian Bach").exists() |
||||
.jsonPath(composerByName, "Johannes Brahms").exists() |
||||
.jsonPath(composerByName, "Edvard Grieg").exists() |
||||
.jsonPath(composerByName, "Robert Schumann").exists() |
||||
.jsonPath(performerByName, "Vladimir Ashkenazy").exists() |
||||
.jsonPath(performerByName, "Yehudi Menuhin").exists() |
||||
.jsonPath("$.composers[0]").exists() |
||||
.jsonPath("$.composers[1]").exists() |
||||
.jsonPath("$.composers[2]").exists() |
||||
.jsonPath("$.composers[3]").exists(); |
||||
} |
||||
|
||||
@Test |
||||
public void doesNotExist() { |
||||
client.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.jsonPath("$.composers[?(@.name == 'Edvard Grieeeeeeg')]").doesNotExist() |
||||
.jsonPath("$.composers[?(@.name == 'Robert Schuuuuuuman')]").doesNotExist() |
||||
.jsonPath("$.composers[4]").doesNotExist(); |
||||
} |
||||
|
||||
@Test |
||||
public void equality() { |
||||
client.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.jsonPath("$.composers[0].name").isEqualTo("Johann Sebastian Bach") |
||||
.jsonPath("$.performers[1].name").isEqualTo("Yehudi Menuhin"); |
||||
|
||||
// Hamcrest matchers...
|
||||
client.get().uri("/music/people") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON) |
||||
.expectBody() |
||||
.jsonPath("$.composers[0].name").value(equalTo("Johann Sebastian Bach")) |
||||
.jsonPath("$.performers[1].name").value(equalTo("Yehudi Menuhin")); |
||||
} |
||||
|
||||
@Test |
||||
public void hamcrestMatcher() { |
||||
client.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.jsonPath("$.composers[0].name").value(startsWith("Johann")) |
||||
.jsonPath("$.performers[0].name").value(endsWith("Ashkenazy")) |
||||
.jsonPath("$.performers[1].name").value(containsString("di Me")) |
||||
.jsonPath("$.composers[1].name").value(is(in(Arrays.asList("Johann Sebastian Bach", "Johannes Brahms")))); |
||||
} |
||||
|
||||
@Test |
||||
public void hamcrestMatcherWithParameterizedJsonPath() { |
||||
String composerName = "$.composers[%s].name"; |
||||
String performerName = "$.performers[%s].name"; |
||||
|
||||
client.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.jsonPath(composerName, 0).value(startsWith("Johann")) |
||||
.jsonPath(performerName, 0).value(endsWith("Ashkenazy")) |
||||
.jsonPath(performerName, 1).value(containsString("di Me")) |
||||
.jsonPath(composerName, 1).value(is(in(Arrays.asList("Johann Sebastian Bach", "Johannes Brahms")))); |
||||
} |
||||
|
||||
|
||||
@RestController |
||||
private class MusicController { |
||||
|
||||
@RequestMapping("/music/people") |
||||
public MultiValueMap<String, Person> get() { |
||||
MultiValueMap<String, Person> map = new LinkedMultiValueMap<>(); |
||||
|
||||
map.add("composers", new Person("Johann Sebastian Bach")); |
||||
map.add("composers", new Person("Johannes Brahms")); |
||||
map.add("composers", new Person("Edvard Grieg")); |
||||
map.add("composers", new Person("Robert Schumann")); |
||||
|
||||
map.add("performers", new Person("Vladimir Ashkenazy")); |
||||
map.add("performers", new Person("Yehudi Menuhin")); |
||||
|
||||
return map; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import javax.validation.Valid; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.ResultActions; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.ui.Model; |
||||
import org.springframework.validation.BindingResult; |
||||
import org.springframework.web.bind.annotation.ControllerAdvice; |
||||
import org.springframework.web.bind.annotation.ModelAttribute; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
||||
import static org.hamcrest.Matchers.allOf; |
||||
import static org.hamcrest.Matchers.endsWith; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.hasProperty; |
||||
import static org.hamcrest.Matchers.notNullValue; |
||||
import static org.hamcrest.Matchers.nullValue; |
||||
import static org.hamcrest.Matchers.startsWith; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.ModelAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ModelAssertionTests { |
||||
|
||||
private final WebTestClient client = |
||||
MockMvcTestClient.bindToController(new SampleController("a string value", 3, new Person("a name"))) |
||||
.controllerAdvice(new ModelAttributeAdvice()) |
||||
.alwaysExpect(status().isOk()) |
||||
.build(); |
||||
|
||||
@Test |
||||
void attributeEqualTo() throws Exception { |
||||
performRequest(HttpMethod.GET, "/") |
||||
.andExpect(model().attribute("integer", 3)) |
||||
.andExpect(model().attribute("string", "a string value")) |
||||
.andExpect(model().attribute("integer", equalTo(3))) // Hamcrest...
|
||||
.andExpect(model().attribute("string", equalTo("a string value"))) |
||||
.andExpect(model().attribute("globalAttrName", equalTo("Global Attribute Value"))); |
||||
} |
||||
|
||||
@Test |
||||
void attributeExists() throws Exception { |
||||
performRequest(HttpMethod.GET, "/") |
||||
.andExpect(model().attributeExists("integer", "string", "person")) |
||||
.andExpect(model().attribute("integer", notNullValue())) // Hamcrest...
|
||||
.andExpect(model().attribute("INTEGER", nullValue())); |
||||
} |
||||
|
||||
@Test |
||||
void attributeHamcrestMatchers() throws Exception { |
||||
performRequest(HttpMethod.GET, "/") |
||||
.andExpect(model().attribute("integer", equalTo(3))) |
||||
.andExpect(model().attribute("string", allOf(startsWith("a string"), endsWith("value")))) |
||||
.andExpect(model().attribute("person", hasProperty("name", equalTo("a name")))); |
||||
} |
||||
|
||||
@Test |
||||
void hasErrors() throws Exception { |
||||
performRequest(HttpMethod.POST, "/persons").andExpect(model().attributeHasErrors("person")); |
||||
} |
||||
|
||||
@Test |
||||
void hasNoErrors() throws Exception { |
||||
performRequest(HttpMethod.GET, "/").andExpect(model().hasNoErrors()); |
||||
} |
||||
|
||||
private ResultActions performRequest(HttpMethod method, String uri) { |
||||
EntityExchangeResult<Void> result = client.method(method).uri(uri).exchange().expectBody().isEmpty(); |
||||
return MockMvcTestClient.resultActionsFor(result); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class SampleController { |
||||
|
||||
private final Object[] values; |
||||
|
||||
SampleController(Object... values) { |
||||
this.values = values; |
||||
} |
||||
|
||||
@RequestMapping("/") |
||||
String handle(Model model) { |
||||
for (Object value : this.values) { |
||||
model.addAttribute(value); |
||||
} |
||||
return "view"; |
||||
} |
||||
|
||||
@PostMapping("/persons") |
||||
String create(@Valid Person person, BindingResult result, Model model) { |
||||
return "view"; |
||||
} |
||||
} |
||||
|
||||
@ControllerAdvice |
||||
private static class ModelAttributeAdvice { |
||||
|
||||
@ModelAttribute("globalAttrName") |
||||
String getAttribute() { |
||||
return "Global Attribute Value"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.ResultActions; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.servlet.HandlerMapping; |
||||
|
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.hasItem; |
||||
import static org.hamcrest.Matchers.not; |
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.RequestAttributeAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class RequestAttributeAssertionTests { |
||||
|
||||
private final WebTestClient mainServletClient = |
||||
MockMvcTestClient.bindToController(new SimpleController()) |
||||
.defaultRequest(get("/").servletPath("/main")) |
||||
.build(); |
||||
|
||||
private final WebTestClient client = |
||||
MockMvcTestClient.bindToController(new SimpleController()).build(); |
||||
|
||||
|
||||
@Test |
||||
void requestAttributeEqualTo() throws Exception { |
||||
performRequest(mainServletClient, "/main/1") |
||||
.andExpect(request().attribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, "/{id}")) |
||||
.andExpect(request().attribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/1")); |
||||
} |
||||
|
||||
@Test |
||||
void requestAttributeMatcher() throws Exception { |
||||
String producibleMediaTypes = HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE; |
||||
|
||||
performRequest(client, "/1") |
||||
.andExpect(request().attribute(producibleMediaTypes, hasItem(MediaType.APPLICATION_JSON))) |
||||
.andExpect(request().attribute(producibleMediaTypes, not(hasItem(MediaType.APPLICATION_XML)))); |
||||
|
||||
performRequest(mainServletClient, "/main/1") |
||||
.andExpect(request().attribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, equalTo("/{id}"))) |
||||
.andExpect(request().attribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, equalTo("/1"))); |
||||
} |
||||
|
||||
private ResultActions performRequest(WebTestClient client, String uri) { |
||||
EntityExchangeResult<Void> result = client.get().uri(uri) |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody().isEmpty(); |
||||
|
||||
return MockMvcTestClient.resultActionsFor(result); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class SimpleController { |
||||
|
||||
@GetMapping(path="/{id}", produces="application/json") |
||||
String show() { |
||||
return "view"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,106 @@
@@ -0,0 +1,106 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.util.Locale; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.ResultActions; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.ui.Model; |
||||
import org.springframework.web.bind.annotation.ModelAttribute; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.SessionAttributes; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.is; |
||||
import static org.hamcrest.Matchers.notNullValue; |
||||
import static org.hamcrest.Matchers.nullValue; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.SessionAttributeAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class SessionAttributeAssertionTests { |
||||
|
||||
private final WebTestClient client = |
||||
MockMvcTestClient.bindToController(new SimpleController()) |
||||
.alwaysExpect(status().isOk()) |
||||
.build(); |
||||
|
||||
|
||||
@Test |
||||
void sessionAttributeEqualTo() throws Exception { |
||||
performRequest().andExpect(request().sessionAttribute("locale", Locale.UK)); |
||||
|
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> performRequest().andExpect(request().sessionAttribute("locale", Locale.US))) |
||||
.withMessage("Session attribute 'locale' expected:<en_US> but was:<en_GB>"); |
||||
} |
||||
|
||||
@Test |
||||
void sessionAttributeMatcher() throws Exception { |
||||
performRequest() |
||||
.andExpect(request().sessionAttribute("bogus", is(nullValue()))) |
||||
.andExpect(request().sessionAttribute("locale", is(notNullValue()))) |
||||
.andExpect(request().sessionAttribute("locale", equalTo(Locale.UK))); |
||||
|
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> performRequest().andExpect(request().sessionAttribute("bogus", is(notNullValue())))) |
||||
.withMessageContaining("null"); |
||||
} |
||||
|
||||
@Test |
||||
void sessionAttributeDoesNotExist() throws Exception { |
||||
performRequest().andExpect(request().sessionAttributeDoesNotExist("bogus", "enigma")); |
||||
|
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> performRequest().andExpect(request().sessionAttributeDoesNotExist("locale"))) |
||||
.withMessage("Session attribute 'locale' exists"); |
||||
} |
||||
|
||||
private ResultActions performRequest() { |
||||
EntityExchangeResult<Void> result = client.post().uri("/").exchange().expectBody().isEmpty(); |
||||
return MockMvcTestClient.resultActionsFor(result); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
@SessionAttributes("locale") |
||||
private static class SimpleController { |
||||
|
||||
@ModelAttribute |
||||
void populate(Model model) { |
||||
model.addAttribute("locale", Locale.UK); |
||||
} |
||||
|
||||
@RequestMapping("/") |
||||
String handle() { |
||||
return "view"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,106 @@
@@ -0,0 +1,106 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.core.annotation.AliasFor; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
import org.springframework.web.bind.annotation.ResponseStatus; |
||||
|
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST; |
||||
import static org.springframework.http.HttpStatus.CREATED; |
||||
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; |
||||
import static org.springframework.http.HttpStatus.NOT_IMPLEMENTED; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.StatusAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class StatusAssertionTests { |
||||
|
||||
private final WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new StatusController()).build(); |
||||
|
||||
|
||||
@Test |
||||
public void testStatusInt() { |
||||
testClient.get().uri("/created").exchange().expectStatus().isEqualTo(201); |
||||
testClient.get().uri("/createdWithComposedAnnotation").exchange().expectStatus().isEqualTo(201); |
||||
testClient.get().uri("/badRequest").exchange().expectStatus().isEqualTo(400); |
||||
} |
||||
|
||||
@Test |
||||
public void testHttpStatus() { |
||||
testClient.get().uri("/created").exchange().expectStatus().isCreated(); |
||||
testClient.get().uri("/createdWithComposedAnnotation").exchange().expectStatus().isCreated(); |
||||
testClient.get().uri("/badRequest").exchange().expectStatus().isBadRequest(); |
||||
} |
||||
|
||||
@Test |
||||
public void testMatcher() { |
||||
testClient.get().uri("/badRequest").exchange().expectStatus().value(equalTo(400)); |
||||
} |
||||
|
||||
|
||||
@RequestMapping |
||||
@ResponseStatus |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@interface Get { |
||||
|
||||
@AliasFor(annotation = RequestMapping.class, attribute = "path") |
||||
String[] path() default {}; |
||||
|
||||
@AliasFor(annotation = ResponseStatus.class, attribute = "code") |
||||
HttpStatus status() default INTERNAL_SERVER_ERROR; |
||||
} |
||||
|
||||
@Controller |
||||
private static class StatusController { |
||||
|
||||
@RequestMapping("/created") |
||||
@ResponseStatus(CREATED) |
||||
public @ResponseBody void created(){ |
||||
} |
||||
|
||||
@Get(path = "/createdWithComposedAnnotation", status = CREATED) |
||||
public @ResponseBody void createdWithComposedAnnotation() { |
||||
} |
||||
|
||||
@RequestMapping("/badRequest") |
||||
@ResponseStatus(code = BAD_REQUEST, reason = "Expired token") |
||||
public @ResponseBody void badRequest(){ |
||||
} |
||||
|
||||
@RequestMapping("/notImplemented") |
||||
@ResponseStatus(NOT_IMPLEMENTED) |
||||
public @ResponseBody void notImplemented(){ |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrlPattern; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.UrlAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class UrlAssertionTests { |
||||
|
||||
private final WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new SimpleController()).build(); |
||||
|
||||
|
||||
@Test |
||||
public void testRedirect() { |
||||
testClient.get().uri("/persons") |
||||
.exchange() |
||||
.expectStatus().isFound() |
||||
.expectHeader().location("/persons/1"); |
||||
} |
||||
|
||||
@Test |
||||
public void testRedirectPattern() throws Exception { |
||||
EntityExchangeResult<Void> result = |
||||
testClient.get().uri("/persons").exchange().expectBody().isEmpty(); |
||||
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andExpect(redirectedUrlPattern("/persons/*")); |
||||
} |
||||
|
||||
@Test |
||||
public void testForward() { |
||||
testClient.get().uri("/") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectHeader().valueEquals("Forwarded-Url", "/home"); |
||||
} |
||||
|
||||
@Test |
||||
public void testForwardPattern() throws Exception { |
||||
EntityExchangeResult<Void> result = |
||||
testClient.get().uri("/").exchange().expectBody().isEmpty(); |
||||
|
||||
MockMvcTestClient.resultActionsFor(result) |
||||
.andExpect(forwardedUrlPattern("/ho?e")); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class SimpleController { |
||||
|
||||
@RequestMapping("/persons") |
||||
public String save() { |
||||
return "redirect:/persons/1"; |
||||
} |
||||
|
||||
@RequestMapping("/") |
||||
public String forward() { |
||||
return "forward:/home"; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,72 @@
@@ -0,0 +1,72 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
||||
import static org.hamcrest.Matchers.containsString; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.UrlAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ViewNameAssertionTests { |
||||
|
||||
private final WebTestClient client = |
||||
MockMvcTestClient.bindToController(new SimpleController()) |
||||
.alwaysExpect(status().isOk()) |
||||
.build(); |
||||
|
||||
|
||||
@Test |
||||
public void testEqualTo() throws Exception { |
||||
MockMvcTestClient.resultActionsFor(performRequest()) |
||||
.andExpect(view().name("mySpecialView")) |
||||
.andExpect(view().name(equalTo("mySpecialView"))); |
||||
} |
||||
|
||||
@Test |
||||
public void testHamcrestMatcher() throws Exception { |
||||
MockMvcTestClient.resultActionsFor(performRequest()) |
||||
.andExpect(view().name(containsString("Special"))); |
||||
} |
||||
|
||||
private EntityExchangeResult<Void> performRequest() { |
||||
return client.get().uri("/").exchange().expectBody().isEmpty(); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class SimpleController { |
||||
|
||||
@RequestMapping("/") |
||||
public String handle() { |
||||
return "mySpecialView"; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType; |
||||
import javax.xml.bind.annotation.XmlAccessorType; |
||||
import javax.xml.bind.annotation.XmlElement; |
||||
import javax.xml.bind.annotation.XmlElementWrapper; |
||||
import javax.xml.bind.annotation.XmlRootElement; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.XmlContentAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class XmlContentAssertionTests { |
||||
|
||||
private static final String PEOPLE_XML = |
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + |
||||
"<people><composers>" + |
||||
"<composer><name>Johann Sebastian Bach</name><someBoolean>false</someBoolean><someDouble>21.0</someDouble></composer>" + |
||||
"<composer><name>Johannes Brahms</name><someBoolean>false</someBoolean><someDouble>0.0025</someDouble></composer>" + |
||||
"<composer><name>Edvard Grieg</name><someBoolean>false</someBoolean><someDouble>1.6035</someDouble></composer>" + |
||||
"<composer><name>Robert Schumann</name><someBoolean>false</someBoolean><someDouble>NaN</someDouble></composer>" + |
||||
"</composers></people>"; |
||||
|
||||
|
||||
private final WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new MusicController()) |
||||
.alwaysExpect(status().isOk()) |
||||
.alwaysExpect(content().contentType(MediaType.parseMediaType("application/xml;charset=UTF-8"))) |
||||
.configureClient() |
||||
.defaultHeader(HttpHeaders.ACCEPT, "application/xml;charset=UTF-8") |
||||
.build(); |
||||
|
||||
|
||||
@Test |
||||
public void testXmlEqualTo() { |
||||
testClient.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody().xml(PEOPLE_XML); |
||||
} |
||||
|
||||
@Test |
||||
public void testNodeHamcrestMatcher() { |
||||
testClient.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody().xpath("/people/composers/composer[1]").exists(); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class MusicController { |
||||
|
||||
@RequestMapping(value="/music/people") |
||||
public @ResponseBody PeopleWrapper getPeople() { |
||||
|
||||
List<Person> composers = Arrays.asList( |
||||
new Person("Johann Sebastian Bach").setSomeDouble(21), |
||||
new Person("Johannes Brahms").setSomeDouble(.0025), |
||||
new Person("Edvard Grieg").setSomeDouble(1.6035), |
||||
new Person("Robert Schumann").setSomeDouble(Double.NaN)); |
||||
|
||||
return new PeopleWrapper(composers); |
||||
} |
||||
} |
||||
|
||||
@SuppressWarnings("unused") |
||||
@XmlRootElement(name="people") |
||||
@XmlAccessorType(XmlAccessType.FIELD) |
||||
private static class PeopleWrapper { |
||||
|
||||
@XmlElementWrapper(name="composers") |
||||
@XmlElement(name="composer") |
||||
private List<Person> composers; |
||||
|
||||
public PeopleWrapper() { |
||||
} |
||||
|
||||
public PeopleWrapper(List<Person> composers) { |
||||
this.composers = composers; |
||||
} |
||||
|
||||
public List<Person> getComposers() { |
||||
return this.composers; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,236 @@
@@ -0,0 +1,236 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.web.servlet.samples.client.standalone.resultmatches; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType; |
||||
import javax.xml.bind.annotation.XmlAccessorType; |
||||
import javax.xml.bind.annotation.XmlElement; |
||||
import javax.xml.bind.annotation.XmlElementWrapper; |
||||
import javax.xml.bind.annotation.XmlRootElement; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.web.Person; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.test.web.servlet.client.MockMvcTestClient; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
||||
import static org.hamcrest.Matchers.closeTo; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.notNullValue; |
||||
import static org.hamcrest.Matchers.startsWith; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET; |
||||
import static org.springframework.web.bind.annotation.RequestMethod.HEAD; |
||||
|
||||
/** |
||||
* MockMvcTestClient equivalent of the MockMvc |
||||
* {@link org.springframework.test.web.servlet.samples.standalone.resultmatchers.XpathAssertionTests}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class XpathAssertionTests { |
||||
|
||||
private static final Map<String, String> musicNamespace = |
||||
Collections.singletonMap("ns", "https://example.org/music/people"); |
||||
|
||||
private final WebTestClient testClient = |
||||
MockMvcTestClient.bindToController(new MusicController()) |
||||
.alwaysExpect(status().isOk()) |
||||
.alwaysExpect(content().contentType(MediaType.parseMediaType("application/xml;charset=UTF-8"))) |
||||
.configureClient() |
||||
.defaultHeader(HttpHeaders.ACCEPT, "application/xml;charset=UTF-8") |
||||
.build(); |
||||
|
||||
|
||||
@Test |
||||
public void testExists() { |
||||
String composer = "/ns:people/composers/composer[%s]"; |
||||
String performer = "/ns:people/performers/performer[%s]"; |
||||
|
||||
testClient.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.xpath(composer, musicNamespace, 1).exists() |
||||
.xpath(composer, musicNamespace, 2).exists() |
||||
.xpath(composer, musicNamespace, 3).exists() |
||||
.xpath(composer, musicNamespace, 4).exists() |
||||
.xpath(performer, musicNamespace, 1).exists() |
||||
.xpath(performer, musicNamespace, 2).exists() |
||||
.xpath(composer, musicNamespace, 1).string(notNullValue()); |
||||
} |
||||
|
||||
@Test |
||||
public void testDoesNotExist() { |
||||
String composer = "/ns:people/composers/composer[%s]"; |
||||
String performer = "/ns:people/performers/performer[%s]"; |
||||
|
||||
testClient.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.xpath(composer, musicNamespace, 0).doesNotExist() |
||||
.xpath(composer, musicNamespace, 5).doesNotExist() |
||||
.xpath(performer, musicNamespace, 0).doesNotExist() |
||||
.xpath(performer, musicNamespace, 3).doesNotExist(); |
||||
} |
||||
|
||||
@Test |
||||
public void testString() { |
||||
|
||||
String composerName = "/ns:people/composers/composer[%s]/name"; |
||||
String performerName = "/ns:people/performers/performer[%s]/name"; |
||||
|
||||
testClient.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.xpath(composerName, musicNamespace, 1).isEqualTo("Johann Sebastian Bach") |
||||
.xpath(composerName, musicNamespace, 2).isEqualTo("Johannes Brahms") |
||||
.xpath(composerName, musicNamespace, 3).isEqualTo("Edvard Grieg") |
||||
.xpath(composerName, musicNamespace, 4).isEqualTo("Robert Schumann") |
||||
.xpath(performerName, musicNamespace, 1).isEqualTo("Vladimir Ashkenazy") |
||||
.xpath(performerName, musicNamespace, 2).isEqualTo("Yehudi Menuhin") |
||||
.xpath(composerName, musicNamespace, 1).string(equalTo("Johann Sebastian Bach")) // Hamcrest..
|
||||
.xpath(composerName, musicNamespace, 1).string(startsWith("Johann")) |
||||
.xpath(composerName, musicNamespace, 1).string(notNullValue()); |
||||
} |
||||
|
||||
@Test |
||||
public void testNumber() { |
||||
String expression = "/ns:people/composers/composer[%s]/someDouble"; |
||||
|
||||
testClient.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.xpath(expression, musicNamespace, 1).isEqualTo(21d) |
||||
.xpath(expression, musicNamespace, 2).isEqualTo(.0025) |
||||
.xpath(expression, musicNamespace, 3).isEqualTo(1.6035) |
||||
.xpath(expression, musicNamespace, 4).isEqualTo(Double.NaN) |
||||
.xpath(expression, musicNamespace, 1).number(equalTo(21d)) // Hamcrest..
|
||||
.xpath(expression, musicNamespace, 3).number(closeTo(1.6, .01)); |
||||
} |
||||
|
||||
@Test |
||||
public void testBoolean() { |
||||
String expression = "/ns:people/performers/performer[%s]/someBoolean"; |
||||
|
||||
testClient.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.xpath(expression, musicNamespace, 1).isEqualTo(false) |
||||
.xpath(expression, musicNamespace, 2).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void testNodeCount() { |
||||
testClient.get().uri("/music/people") |
||||
.exchange() |
||||
.expectBody() |
||||
.xpath("/ns:people/composers/composer", musicNamespace).nodeCount(4) |
||||
.xpath("/ns:people/performers/performer", musicNamespace).nodeCount(2) |
||||
.xpath("/ns:people/composers/composer", musicNamespace).nodeCount(equalTo(4)) // Hamcrest..
|
||||
.xpath("/ns:people/performers/performer", musicNamespace).nodeCount(equalTo(2)); |
||||
} |
||||
|
||||
@Test |
||||
public void testFeedWithLinefeedChars() { |
||||
MockMvcTestClient.bindToController(new BlogFeedController()).build() |
||||
.get().uri("/blog.atom") |
||||
.accept(MediaType.APPLICATION_ATOM_XML) |
||||
.exchange() |
||||
.expectBody() |
||||
.xpath("//feed/title").isEqualTo("Test Feed") |
||||
.xpath("//feed/icon").isEqualTo("https://www.example.com/favicon.ico"); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
private static class MusicController { |
||||
|
||||
@RequestMapping(value = "/music/people") |
||||
public @ResponseBody |
||||
PeopleWrapper getPeople() { |
||||
|
||||
List<Person> composers = Arrays.asList( |
||||
new Person("Johann Sebastian Bach").setSomeDouble(21), |
||||
new Person("Johannes Brahms").setSomeDouble(.0025), |
||||
new Person("Edvard Grieg").setSomeDouble(1.6035), |
||||
new Person("Robert Schumann").setSomeDouble(Double.NaN)); |
||||
|
||||
List<Person> performers = Arrays.asList( |
||||
new Person("Vladimir Ashkenazy").setSomeBoolean(false), |
||||
new Person("Yehudi Menuhin").setSomeBoolean(true)); |
||||
|
||||
return new PeopleWrapper(composers, performers); |
||||
} |
||||
} |
||||
|
||||
@SuppressWarnings("unused") |
||||
@XmlRootElement(name = "people", namespace = "https://example.org/music/people") |
||||
@XmlAccessorType(XmlAccessType.FIELD) |
||||
private static class PeopleWrapper { |
||||
|
||||
@XmlElementWrapper(name = "composers") |
||||
@XmlElement(name = "composer") |
||||
private List<Person> composers; |
||||
|
||||
@XmlElementWrapper(name = "performers") |
||||
@XmlElement(name = "performer") |
||||
private List<Person> performers; |
||||
|
||||
public PeopleWrapper() { |
||||
} |
||||
|
||||
public PeopleWrapper(List<Person> composers, List<Person> performers) { |
||||
this.composers = composers; |
||||
this.performers = performers; |
||||
} |
||||
|
||||
public List<Person> getComposers() { |
||||
return this.composers; |
||||
} |
||||
|
||||
public List<Person> getPerformers() { |
||||
return this.performers; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
public class BlogFeedController { |
||||
|
||||
@RequestMapping(value = "/blog.atom", method = {GET, HEAD}) |
||||
@ResponseBody |
||||
public String listPublishedPosts() { |
||||
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" |
||||
+ "<feed xmlns=\"http://www.w3.org/2005/Atom\">\r\n" |
||||
+ " <title>Test Feed</title>\r\n" |
||||
+ " <icon>https://www.example.com/favicon.ico</icon>\r\n" |
||||
+ "</feed>\r\n\r\n"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue