Migrate to Asciidoctor Tabs

This commit is contained in:
Rob Winch
2023-04-20 16:21:36 -05:00
committed by rstoyanchev
parent 71154fd16b
commit 39146f9066
243 changed files with 7124 additions and 1779 deletions

View File

@@ -16,8 +16,11 @@ first, then manually performing the async dispatch, and finally verifying the re
Below is an example test for controller methods that return `DeferredResult`, `Callable`,
or reactive type such as Reactor `Mono`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// static import of MockMvcRequestBuilders.* and MockMvcResultMatchers.*
@@ -34,6 +37,7 @@ or reactive type such as Reactor `Mono`:
.andExpect(content().string("body"));
}
----
======
<1> Check response status is still unchanged
<2> Async processing must have started
<3> Wait and assert the async result

View File

@@ -5,16 +5,20 @@ You can define expectations by appending one or more `andExpect(..)` calls after
performing a request, as the following example shows. As soon as one expectation fails,
no other expectations will be asserted.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// static import of MockMvcRequestBuilders.* and MockMvcResultMatchers.*
mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.test.web.servlet.get
@@ -22,14 +26,18 @@ no other expectations will be asserted.
status { isOk() }
}
----
======
You can define multiple expectations by appending `andExpectAll(..)` after performing a
request, as the following example shows. In contrast to `andExpect(..)`,
`andExpectAll(..)` guarantees that all supplied expectations will be asserted and that
all failures will be tracked and reported.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// static import of MockMvcRequestBuilders.* and MockMvcResultMatchers.*
@@ -38,8 +46,9 @@ all failures will be tracked and reported.
content().contentType("application/json;charset=UTF-8"));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.test.web.servlet.get
@@ -48,6 +57,7 @@ all failures will be tracked and reported.
content { contentType(APPLICATION_JSON) }
}
----
======
`MockMvcResultMatchers.*` provides a number of expectations, some of which are further
nested with more detailed expectations.
@@ -64,16 +74,20 @@ inspect Servlet specific aspects, such as request and session attributes.
The following test asserts that binding or validation failed:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
mockMvc.perform(post("/persons"))
.andExpect(status().isOk())
.andExpect(model().attributeHasErrors("person"));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.test.web.servlet.post
@@ -84,13 +98,17 @@ The following test asserts that binding or validation failed:
}
}
----
======
Many times, when writing tests, it is useful to dump the results of the performed
request. You can do so as follows, where `print()` is a static import from
`MockMvcResultHandlers`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
mockMvc.perform(post("/persons"))
.andDo(print())
@@ -98,8 +116,9 @@ request. You can do so as follows, where `print()` is a static import from
.andExpect(model().attributeHasErrors("person"));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.test.web.servlet.post
@@ -112,6 +131,7 @@ request. You can do so as follows, where `print()` is a static import from
}
}
----
======
As long as request processing does not cause an unhandled exception, the `print()` method
prints all the available result data to `System.out`. There is also a `log()` method and
@@ -126,36 +146,47 @@ In some cases, you may want to get direct access to the result and verify someth
cannot be verified otherwise. This can be achieved by appending `.andReturn()` after all
other expectations, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn();
// ...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
var mvcResult = mockMvc.post("/persons").andExpect { status { isOk() } }.andReturn()
// ...
----
======
If all tests repeat the same expectations, you can set up common expectations once when
building the `MockMvc` instance, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
standaloneSetup(new SimpleController())
.alwaysExpect(status().isOk())
.alwaysExpect(content().contentType("application/json;charset=UTF-8"))
.build()
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
----
======
Note that common expectations are always applied and cannot be overridden without
creating a separate `MockMvc` instance.
@@ -164,15 +195,19 @@ When a JSON response content contains hypermedia links created with
https://github.com/spring-projects/spring-hateoas[Spring HATEOAS], you can verify the
resulting links by using JsonPath expressions, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people"));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
mockMvc.get("/people") {
accept(MediaType.APPLICATION_JSON)
@@ -182,21 +217,26 @@ resulting links by using JsonPath expressions, as the following example shows:
}
}
----
======
When XML response content contains hypermedia links created with
https://github.com/spring-projects/spring-hateoas[Spring HATEOAS], you can verify the
resulting links by using XPath expressions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Map<String, String> ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom");
mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML))
.andExpect(xpath("/person/ns:link[@rel='self']/@href", ns).string("http://localhost:8080/people"));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ns = mapOf("ns" to "http://www.w3.org/2005/Atom")
mockMvc.get("/handle") {
@@ -207,4 +247,5 @@ resulting links by using XPath expressions:
}
}
----
======

View File

@@ -5,16 +5,22 @@
When setting up a `MockMvc` instance, you can register one or more Servlet `Filter`
instances, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
----
======
Registered filters are invoked through the `MockFilterChain` from `spring-test`, and the
last filter delegates to the `DispatcherServlet`.

View File

@@ -14,8 +14,11 @@ First, make sure that you have included a test dependency on
We can easily create an HtmlUnit `WebClient` that integrates with MockMvc by using the
`MockMvcWebClientBuilder`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient webClient;
@@ -27,8 +30,9 @@ We can easily create an HtmlUnit `WebClient` that integrates with MockMvc by usi
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
lateinit var webClient: WebClient
@@ -39,6 +43,7 @@ We can easily create an HtmlUnit `WebClient` that integrates with MockMvc by usi
.build()
}
----
======
NOTE: This is a simple example of using `MockMvcWebClientBuilder`. For advanced usage,
see xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
@@ -55,17 +60,22 @@ Now we can use HtmlUnit as we normally would but without the need to deploy our
application to a Servlet container. For example, we can request the view to create a
message with the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val createMsgFormPage = webClient.getPage("http://localhost/messages/form")
----
======
NOTE: The default context path is `""`. Alternatively, we can specify the context path,
as described in xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
@@ -73,8 +83,11 @@ as described in xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc#
Once we have a reference to the `HtmlPage`, we can then fill out the form and submit it
to create a message, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
@@ -84,8 +97,10 @@ to create a message, as the following example shows:
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");
HtmlPage newMessagePage = submit.click();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val form = createMsgFormPage.getHtmlElementById("messageForm")
val summaryInput = createMsgFormPage.getHtmlElementById("summary")
@@ -95,12 +110,16 @@ to create a message, as the following example shows:
val submit = form.getOneHtmlElementByAttribute("input", "type", "submit")
val newMessagePage = submit.click()
----
======
Finally, we can verify that a new message was created successfully. The following
assertions use the https://assertj.github.io/doc/[AssertJ] library:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");
String id = newMessagePage.getHtmlElementById("id").getTextContent();
@@ -110,8 +129,10 @@ assertions use the https://assertj.github.io/doc/[AssertJ] library:
String text = newMessagePage.getHtmlElementById("text").getTextContent();
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123")
val id = newMessagePage.getHtmlElementById("id").getTextContent()
@@ -121,6 +142,7 @@ assertions use the https://assertj.github.io/doc/[AssertJ] library:
val text = newMessagePage.getHtmlElementById("text").getTextContent()
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!")
----
======
The preceding code improves on our
xref:testing/spring-mvc-test-framework/server-htmlunit/why.adoc#spring-mvc-test-server-htmlunit-mock-mvc-test[MockMvc test] in a number of ways.
@@ -142,8 +164,11 @@ In the examples so far, we have used `MockMvcWebClientBuilder` in the simplest w
possible, by building a `WebClient` based on the `WebApplicationContext` loaded for us by
the Spring TestContext Framework. This approach is repeated in the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient webClient;
@@ -155,8 +180,9 @@ the Spring TestContext Framework. This approach is repeated in the following exa
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
lateinit var webClient: WebClient
@@ -167,11 +193,15 @@ the Spring TestContext Framework. This approach is repeated in the following exa
.build()
}
----
======
We can also specify additional configuration options, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient webClient;
@@ -188,8 +218,10 @@ We can also specify additional configuration options, as the following example s
.build();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
lateinit var webClient: WebClient
@@ -206,12 +238,16 @@ We can also specify additional configuration options, as the following example s
.build()
}
----
======
As an alternative, we can perform the exact same setup by configuring the `MockMvc`
instance separately and supplying it to the `MockMvcWebClientBuilder`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
MockMvc mockMvc = MockMvcBuilders
.webAppContextSetup(context)
@@ -228,11 +264,13 @@ instance separately and supplying it to the `MockMvcWebClientBuilder`, as follow
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
----
======
This is more verbose, but, by building the `WebClient` with a `MockMvc` instance, we have
the full power of MockMvc at our fingertips.

View File

@@ -26,25 +26,34 @@ afterwards.
If one of the fields were named "`summary`", we might have something that resembles the
following repeated in multiple places within our tests:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
summaryInput.setValueAttribute(summary);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val summaryInput = currentPage.getHtmlElementById("summary")
summaryInput.setValueAttribute(summary)
----
======
So what happens if we change the `id` to `smmry`? Doing so would force us to update all
of our tests to incorporate this change. This violates the DRY principle, so we should
ideally extract this code into its own method, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
setSummary(currentPage, summary);
@@ -57,8 +66,9 @@ ideally extract this code into its own method, as follows:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun createMessage(currentPage: HtmlPage, summary:String, text:String) :HtmlPage{
setSummary(currentPage, summary);
@@ -70,14 +80,18 @@ ideally extract this code into its own method, as follows:
summaryInput.setValueAttribute(summary)
}
----
======
Doing so ensures that we do not have to update all of our tests if we change the UI.
We might even take this a step further and place this logic within an `Object` that
represents the `HtmlPage` we are currently on, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class CreateMessagePage {
@@ -111,8 +125,10 @@ represents the `HtmlPage` we are currently on, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class CreateMessagePage(private val currentPage: HtmlPage) {
@@ -139,6 +155,7 @@ represents the `HtmlPage` we are currently on, as the following example shows:
}
}
----
======
Formerly, this pattern was known as the
https://github.com/SeleniumHQ/selenium/wiki/PageObjects[Page Object Pattern]. While we
@@ -154,8 +171,11 @@ includes a test dependency on `org.seleniumhq.selenium:selenium-htmlunit-driver`
We can easily create a Selenium WebDriver that integrates with MockMvc by using the
`MockMvcHtmlUnitDriverBuilder` as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebDriver driver;
@@ -166,8 +186,10 @@ We can easily create a Selenium WebDriver that integrates with MockMvc by using
.build();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
lateinit var driver: WebDriver
@@ -178,6 +200,7 @@ We can easily create a Selenium WebDriver that integrates with MockMvc by using
.build()
}
----
======
NOTE: This is a simple example of using `MockMvcHtmlUnitDriverBuilder`. For more advanced
usage, see xref:testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
@@ -195,35 +218,45 @@ application to a Servlet container. For example, we can request the view to crea
message with the following:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
CreateMessagePage page = CreateMessagePage.to(driver);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val page = CreateMessagePage.to(driver)
----
======
--
We can then fill out the form and submit it to create a message, as follows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ViewMessagePage viewMessagePage =
page.createMessage(ViewMessagePage.class, expectedSummary, expectedText);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val viewMessagePage =
page.createMessage(ViewMessagePage::class, expectedSummary, expectedText)
----
======
--
This improves on the design of our xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test]
@@ -233,8 +266,11 @@ with HtmlUnit, but it is much easier with WebDriver. Consider the following
`CreateMessagePage` implementation:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class CreateMessagePage extends AbstractPage { // <1>
@@ -262,6 +298,7 @@ with HtmlUnit, but it is much easier with WebDriver. Consider the following
}
}
----
======
<1> `CreateMessagePage` extends the `AbstractPage`. We do not go over the details of
`AbstractPage`, but, in summary, it contains common functionality for all of our pages.
For example, if our application has a navigational bar, global error messages, and other
@@ -327,26 +364,35 @@ Finally, we can verify that a new message was created successfully. The followin
assertions use the https://assertj.github.io/doc/[AssertJ] assertion library:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
assertThat(viewMessagePage.message).isEqualTo(expectedMessage)
assertThat(viewMessagePage.success).isEqualTo("Successfully created a new message")
----
======
--
We can see that our `ViewMessagePage` lets us interact with our custom domain model. For
example, it exposes a method that returns a `Message` object:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public Message getMessage() throws ParseException {
Message message = new Message();
@@ -357,11 +403,14 @@ example, it exposes a method that returns a `Message` object:
return message;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun getMessage() = Message(getId(), getCreated(), getSummary(), getText())
----
======
--
We can then use the rich domain objects in our assertions.
@@ -370,8 +419,11 @@ Lastly, we must not forget to close the `WebDriver` instance when the test is co
as follows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@AfterEach
void destroy() {
@@ -381,8 +433,9 @@ as follows:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@AfterEach
fun destroy() {
@@ -391,6 +444,7 @@ as follows:
}
}
----
======
--
For additional information on using WebDriver, see the Selenium
@@ -403,8 +457,11 @@ In the examples so far, we have used `MockMvcHtmlUnitDriverBuilder` in the simpl
possible, by building a `WebDriver` based on the `WebApplicationContext` loaded for us by
the Spring TestContext Framework. This approach is repeated here, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebDriver driver;
@@ -416,8 +473,9 @@ the Spring TestContext Framework. This approach is repeated here, as follows:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
lateinit var driver: WebDriver
@@ -428,11 +486,15 @@ the Spring TestContext Framework. This approach is repeated here, as follows:
.build()
}
----
======
We can also specify additional configuration options, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebDriver driver;
@@ -449,8 +511,10 @@ We can also specify additional configuration options, as follows:
.build();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
lateinit var driver: WebDriver
@@ -467,12 +531,16 @@ We can also specify additional configuration options, as follows:
.build()
}
----
======
As an alternative, we can perform the exact same setup by configuring the `MockMvc`
instance separately and supplying it to the `MockMvcHtmlUnitDriverBuilder`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
MockMvc mockMvc = MockMvcBuilders
.webAppContextSetup(context)
@@ -489,11 +557,13 @@ instance separately and supplying it to the `MockMvcHtmlUnitDriverBuilder`, as f
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
----
======
This is more verbose, but, by building the `WebDriver` with a `MockMvc` instance, we have
the full power of MockMvc at our fingertips.

View File

@@ -8,8 +8,11 @@ supports paging through all messages. How would you go about testing it?
With Spring MVC Test, we can easily test if we are able to create a `Message`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
MockHttpServletRequestBuilder createMessage = post("/messages/")
.param("summary", "Spring Rocks")
@@ -19,8 +22,10 @@ With Spring MVC Test, we can easily test if we are able to create a `Message`, a
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/messages/123"));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Test
fun test() {
@@ -33,6 +38,7 @@ With Spring MVC Test, we can easily test if we are able to create a `Message`, a
}
}
----
======
What if we want to test the form view that lets us create the message? For example,
assume our form looks like the following snippet:
@@ -57,31 +63,39 @@ assume our form looks like the following snippet:
How do we ensure that our form produce the correct request to create a new message? A
naive attempt might resemble the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
mockMvc.perform(get("/messages/form"))
.andExpect(xpath("//input[@name='summary']").exists())
.andExpect(xpath("//textarea[@name='text']").exists());
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
mockMvc.get("/messages/form").andExpect {
xpath("//input[@name='summary']") { exists() }
xpath("//textarea[@name='text']") { exists() }
}
----
======
This test has some obvious drawbacks. If we update our controller to use the parameter
`message` instead of `text`, our form test continues to pass, even though the HTML form
is out of synch with the controller. To resolve this we can combine our two tests, as
follows:
[tabs]
======
Java::
+
[[spring-mvc-test-server-htmlunit-mock-mvc-test]]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
String summaryParamName = "summary";
String textParamName = "text";
@@ -98,8 +112,9 @@ follows:
.andExpect(redirectedUrl("/messages/123"));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val summaryParamName = "summary";
val textParamName = "text";
@@ -115,6 +130,7 @@ follows:
redirectedUrl("/messages/123")
}
----
======
This would reduce the risk of our test incorrectly passing, but there are still some
problems:

View File

@@ -7,16 +7,20 @@ xref:testing/webtestclient.adoc#webtestclient-tests[Writing Tests] instead.
To perform requests that use any HTTP method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// static import of MockMvcRequestBuilders.*
mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.test.web.servlet.post
@@ -24,19 +28,24 @@ To perform requests that use any HTTP method, as the following example shows:
accept = MediaType.APPLICATION_JSON
}
----
======
You can also perform file upload requests that internally use
`MockMultipartHttpServletRequest` so that there is no actual parsing of a multipart
request. Rather, you have to set it up to be similar to the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
mockMvc.perform(multipart("/doc").file("a1", "ABC".getBytes("UTF-8")));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.test.web.servlet.multipart
@@ -44,30 +53,42 @@ request. Rather, you have to set it up to be similar to the following example:
file("a1", "ABC".toByteArray(charset("UTF8")))
}
----
======
You can specify query parameters in URI template style, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
mockMvc.perform(get("/hotels?thing={thing}", "somewhere"));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
mockMvc.get("/hotels?thing={thing}", "somewhere")
----
======
You can also add Servlet request parameters that represent either query or form
parameters, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
mockMvc.perform(get("/hotels").param("thing", "somewhere"));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.test.web.servlet.get
@@ -75,6 +96,7 @@ parameters, as the following example shows:
param("thing", "somewhere")
}
----
======
If application code relies on Servlet request parameters and does not check the query
string explicitly (as is most often the case), it does not matter which option you use.
@@ -87,13 +109,18 @@ request URI. If you must test with the full request URI, be sure to set the `con
and `servletPath` accordingly so that request mappings work, as the following example
shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.test.web.servlet.get
@@ -102,13 +129,17 @@ shows:
servletPath = "/main"
}
----
======
In the preceding example, it would be cumbersome to set the `contextPath` and
`servletPath` with every performed request. Instead, you can set up default request
properties, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
class MyWebTests {
@@ -123,11 +154,14 @@ properties, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
----
======
The preceding properties affect every request performed through the `MockMvc` instance.
If the same property is also specified on a given request, it overrides the default

View File

@@ -7,8 +7,11 @@ point to Spring configuration with Spring MVC and controller infrastructure in i
To set up MockMvc for testing a specific controller, use the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
class MyWebTests {
@@ -24,8 +27,9 @@ To set up MockMvc for testing a specific controller, use the following:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyWebTests {
@@ -40,6 +44,7 @@ To set up MockMvc for testing a specific controller, use the following:
}
----
======
Or you can also use this setup when testing through the
xref:testing/webtestclient.adoc#webtestclient-controller-config[WebTestClient] which delegates to the same builder
@@ -47,8 +52,11 @@ as shown above.
To set up MockMvc through Spring configuration, use the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@SpringJUnitWebConfig(locations = "my-servlet-context.xml")
class MyWebTests {
@@ -65,8 +73,9 @@ To set up MockMvc through Spring configuration, use the following:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitWebConfig(locations = ["my-servlet-context.xml"])
class MyWebTests {
@@ -82,6 +91,7 @@ To set up MockMvc through Spring configuration, use the following:
}
----
======
Or you can also use this setup when testing through the
xref:testing/webtestclient.adoc#webtestclient-context-config[WebTestClient] which delegates to the same builder
@@ -108,8 +118,11 @@ a mock service with Mockito:
You can then inject the mock service into the test to set up and verify your
expectations, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@SpringJUnitWebConfig(locations = "test-servlet-context.xml")
class AccountTests {
@@ -128,8 +141,10 @@ expectations, as the following example shows:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitWebConfig(locations = ["test-servlet-context.xml"])
class AccountTests {
@@ -148,6 +163,7 @@ expectations, as the following example shows:
}
----
======
The `standaloneSetup`, on the other hand, is a little closer to a unit test. It tests one
controller at a time. You can manually inject the controller with mock dependencies, and

View File

@@ -6,8 +6,11 @@ some common and very useful features. For example, you can declare an `Accept` h
all requests and expect a status of 200 as well as a `Content-Type` header in all
responses, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// static import of MockMvcBuilders.standaloneSetup
@@ -18,19 +21,24 @@ responses, as follows:
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
----
======
In addition, third-party frameworks (and applications) can pre-package setup
instructions, such as those in a `MockMvcConfigurer`. The Spring Framework has one such
built-in implementation that helps to save and re-use the HTTP session across requests.
You can use it as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// static import of SharedHttpSessionConfigurer.sharedHttpSession
@@ -41,11 +49,13 @@ You can use it as follows:
// Use mockMvc to perform requests...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
----
======
See the javadoc for
{api-spring-framework}/test/web/servlet/setup/ConfigurableMockMvcBuilder.html[`ConfigurableMockMvcBuilder`]

View File

@@ -5,8 +5,11 @@ The best way to test streaming responses such as Server-Sent Events is through t
<<WebTestClient>> which can be used as a test client to connect to a `MockMvc` instance
to perform tests on Spring MVC controllers without a running server. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebTestClient client = MockMvcWebTestClient.bindToController(new SseController()).build();
@@ -26,6 +29,7 @@ to perform tests on Spring MVC controllers without a running server. For example
.thenCancel()
.verify();
----
======
`WebTestClient` can also connect to a live server and perform full end-to-end integration
tests. This is also supported in Spring Boot where you can