Browse Source

Polish testing chapter

pull/884/head
Sam Brannen 9 years ago
parent
commit
26798cc338
  1. 130
      src/asciidoc/testing.adoc

130
src/asciidoc/testing.adoc

@ -1213,9 +1213,9 @@ configuration. @@ -1213,9 +1213,9 @@ configuration.
In addition to generic testing infrastructure, the TestContext framework provides
explicit support for JUnit and TestNG in the form of `abstract` support classes. For
JUnit, Spring also provides a custom JUnit `Runner` that allows one to write so-called
__POJO test classes__. POJO test classes are not required to extend a particular class
hierarchy.
JUnit, Spring also provides a custom JUnit `Runner` and custom JUnit `Rules` that allow
one to write so-called __POJO test classes__. POJO test classes are not required to
extend a particular class hierarchy.
The following section provides an overview of the internals of the TestContext
framework. If you are only interested in using the framework and not necessarily
@ -3759,23 +3759,22 @@ The __Spring MVC Test framework__ provides first class support for testing Sprin @@ -3759,23 +3759,22 @@ The __Spring MVC Test framework__ provides first class support for testing Sprin
code using a fluent API that can be used with JUnit, TestNG, or any other testing
framework. It's built on the
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/mock/web/package-summary.html[Servlet API mock objects]
from the `spring-test` module and hence does not require a running Servlet container,
it uses the `DispatcherServlet` thus providing full Spring MVC support, and
may optionally load actual Spring configuration with the __TestContext framework__
in addition to a standalone mode in which controllers may be instantiated manually
and tested one at a time.
from the `spring-test` module and hence does _not_ use a running Servlet container. It
uses the `DispatcherServlet` to provide full Spring MVC runtime behavior and provides support
for loading actual Spring configuration with the __TestContext framework__ in addition to a
standalone mode in which controllers may be instantiated manually and tested one at a time.
__Spring MVC Test__ also provides client-side support for testing code that uses
the `RestTemplate`. Client-side tests mock the server responses and also do not
require a running server.
the `RestTemplate`. Client-side tests mock the server responses and also do _not_
use a running server.
[TIP]
====
Spring Boot provides an option to write full, end-to-end integration tests that include
a running server. If this is your goal please have a look at the
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications[Spring Boot reference page].
For more on the difference with end-to-end integration tests see
<<spring-mvc-test-vs-end-to-end-integration-tests>>.
For more information on the differences between out-of-container and end-to-end
integration tests, see <<spring-mvc-test-vs-end-to-end-integration-tests>>.
====
@ -3790,8 +3789,8 @@ mappings, data binding, type conversion, validation, and much more. Furthermore, @@ -3790,8 +3789,8 @@ mappings, data binding, type conversion, validation, and much more. Furthermore,
controller methods such as `@InitBinder`, `@ModelAttribute`, and `@ExceptionHandler` may
also be invoked as part of the request processing lifecycle.
The goal of __Spring MVC Test__ is to provide an effective way of testing controllers
by performing requests and generating responses through the `DispatcherServlet`.
The goal of __Spring MVC Test__ is to provide an effective way for testing controllers
by performing requests and generating responses through the actual `DispatcherServlet`.
__Spring MVC Test__ builds on the familiar <<mock-objects-servlet,"mock" implementations
of the Servlet API>> available in the `spring-test` module. This allows performing
@ -3832,13 +3831,13 @@ JUnit-based example of using Spring MVC Test: @@ -3832,13 +3831,13 @@ JUnit-based example of using Spring MVC Test:
----
The above test relies on the `WebApplicationContext` support of the __TestContext framework__
to loads Spring configuration from an XML configuration file located in the same package
as the test class but also supported is Java-based configuration. See these
for loading Spring configuration from an XML configuration file located in the same package
as the test class, but Java-based and Groovy-based configuration are also supported. See these
https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context[sample tests].
The `MockMvc` instance is used to perform a request to `"/accounts/1"` and verify the
resulting response has status 200, content type is `"application/json"`, and
response body has a JSON property called "name" with the value "Lee". The jsonPath
The `MockMvc` instance is used to perform a `GET` request to `"/accounts/1"` and verify
that the resulting response has status 200, the content type is `"application/json"`, and the
response body has a JSON property called "name" with the value "Lee". The `jsonPath`
syntax is supported through the Jayway https://github.com/jayway/JsonPath[JsonPath
project]. There are lots of other options for verifying the result of the performed
request that will be discussed below.
@ -3860,7 +3859,7 @@ completion on static members. @@ -3860,7 +3859,7 @@ completion on static members.
There are two main options for creating an instance of `MockMvc`.
The first is to load Spring MVC configuration through the __TestContext
framework__, which loads the Spring configuration and injects a `WebApplicationContext`
into the test to use to create a `MockMvc`:
into the test to use to build a `MockMvc` instance:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -4000,7 +3999,7 @@ Or you can add Servlet request parameters representing either query of form para @@ -4000,7 +3999,7 @@ Or you can add Servlet request parameters representing either query of form para
If application code relies on Servlet request parameters and doesn't check the query
string explicitly (as is most often the case) then it doesn't matter which option you use.
Keep in mind however that query params provided with the URI template will be decoded while
request parameters provided through the `param(...)` method are expected to be decoded.
request parameters provided through the `param(...)` method are expected to already be decoded.
In most cases it's preferable to leave out the context path and the Servlet path from
the request URI. If you must test with the full request URI, be sure to set the
@ -4048,19 +4047,20 @@ performing a request: @@ -4048,19 +4047,20 @@ performing a request:
mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
----
`MockMvcResultMatchers.*` provides a number of expectations some of which are further
`MockMvcResultMatchers.*` provides a number of expectations, some of which are further
nested with more detailed expectations.
Expectations fall in two general categories. The first category of assertions verify
properties of the response, i.e the response status, headers, and content. Those
Expectations fall in two general categories. The first category of assertions verifies
properties of the response: for example, the response status, headers, and content. These
are the most important results to assert.
The second category of assertions go beyond the response. They allow inspecting Spring
MVC specific things such as which controller method processed the request, whether
an exception was raised and handled, what the content of the model is, what view was
selected, what flash attributes were added, and so on. They also allow inspecting
Servlet specific things such as request and session attributes. The following test
asserts that binding/validation failed:
The second category of assertions goes beyond the response. These assertions allow
one to inspect Spring MVC specific aspects such as which controller method processed
the request, whether an exception was raised and handled, what the content of the model
is, what view was selected, what flash attributes were added, and so on. They also allow
one to inspect Servlet specific aspects such as request and session attributes.
The following test asserts that binding or validation failed:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -4085,7 +4085,7 @@ This can be done as follows, where `print()` is a static import from @@ -4085,7 +4085,7 @@ This can be done as follows, where `print()` is a static import from
As long as request processing does not cause an unhandled exception, the `print()` method
will print all the available result data to `System.out`. Spring Framework 4.2 introduces
a new `log()` method and two additional variants of the `print()` method: one that accepts
a `log()` method and two additional variants of the `print()` method, one that accepts
an `OutputStream` and one that accepts a `Writer`. For example, invoking
`print(System.err)` will print the result data to `System.err`; while invoking
`print(myWriter)` will print the result data to a custom writer. If you would like to
@ -4094,8 +4094,8 @@ will log the result data as a single `DEBUG` message under the @@ -4094,8 +4094,8 @@ will log the result data as a single `DEBUG` message under the
`org.springframework.test.web.servlet.result` logging category.
In some cases, you may want to get direct access to the result and verify something that
cannot be verified otherwise. This can be done by appending `.andReturn()` at the end
after all expectations:
cannot be verified otherwise. This can be achieved by appending `.andReturn()` after all
other expectations:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -4104,7 +4104,7 @@ after all expectations: @@ -4104,7 +4104,7 @@ after all expectations:
// ...
----
When all tests repeat the same expectations you can set up common expectations once
If all tests repeat the same expectations you can set up common expectations once
when building the `MockMvc` instance:
[source,java,indent=0]
@ -4116,12 +4116,12 @@ when building the `MockMvc` instance: @@ -4116,12 +4116,12 @@ when building the `MockMvc` instance:
.build()
----
Note that the expectation is __always__ applied and cannot be overridden without
Note that common expectations are __always__ applied and cannot be overridden without
creating a separate `MockMvc` instance.
When JSON response content contains hypermedia links created with
https://github.com/spring-projects/spring-hateoas[Spring HATEOAS], the resulting links can
be verified:
be verified using JsonPath expressions:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -4132,7 +4132,7 @@ be verified: @@ -4132,7 +4132,7 @@ be verified:
When XML response content contains hypermedia links created with
https://github.com/spring-projects/spring-hateoas[Spring HATEOAS], the resulting links can
be verified:
be verified using XPath expressions:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -4144,7 +4144,7 @@ be verified: @@ -4144,7 +4144,7 @@ be verified:
[[spring-mvc-test-server-filters]]
===== Filter Registrations
When setting up a `MockMvc`, you can register one or more `Filter` instances:
When setting up a `MockMvc` instance, you can register one or more Servlet `Filter` instances:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -4152,53 +4152,55 @@ When setting up a `MockMvc`, you can register one or more `Filter` instances: @@ -4152,53 +4152,55 @@ When setting up a `MockMvc`, you can register one or more `Filter` instances:
mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();
----
Registered filters will be invoked through `MockFilterChain` from `spring-test` and the
last filter will delegates to the `DispatcherServlet`.
Registered filters will be invoked through via the `MockFilterChain` from `spring-test`, and the
last filter will delegate to the `DispatcherServlet`.
[[spring-mvc-test-vs-end-to-end-integration-tests]]
===== Difference With End-to-End Integration Tests
===== Differences between Out-of-Container and End-to-End Integration Tests
As mentioned earlier __Spring MVC Test__ is built on the Servlet API mock objects from
the `spring-test` module and does not rely on a running Servlet container. Therefore
the `spring-test` module and does not use a running Servlet container. Therefore
there are some important differences compared to full end-to-end integration tests
with an actual client and server running.
The easiest way to think about this is starting with a blank `MockHttpServletRequest`.
Whatever you add to it is what the request will be. The things that may catch you out are
there is no context path by default, no jsessionid cookie, no forwarding, error, or async
dispatches, and therefore no actual JSP rendering. Instead "forwarded" and "redirected"
URLs are saved in the `MockHttpServletResponse` and can be asserted with expectations.
Whatever you add to it is what the request will be. Things that may catch you by surprise
are that there is no context path by default, no `jsessionid` cookie, no forwarding, error,
or async dispatches, and therefore no actual JSP rendering. Instead, "forwarded" and
"redirected" URLs are saved in the `MockHttpServletResponse` and can be asserted with
expectations.
This means if you are using JSPs you can verify the JSP page to which the request was
forwarded but there won't be any HTML rendered. Note however that all other rendering
technologies that don't rely on forwarding such as Thymeleaf, Freemarker, Velocity
will render HTML to the response body as expected. The same is true for rendering JSON,
XML and others via `@ResponseBody` methods.
forwarded, but there won't be any HTML rendered. In other words, the JSP will not be
_invoked_. Note however that all other rendering technologies which don't rely on
forwarding such as Thymeleaf, Freemarker, and Velocity will render HTML to the response
body as expected. The same is true for rendering JSON, XML, and other formats via
`@ResponseBody` methods.
Alternatively you may consider the full end-to-end integration testing support from
Spring Boot via `@WebIntegrationTest`. See the
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications[Spring Boot reference].
There are pros and cons for each. The options provided in __Spring MVC Test__
are different stops on the scale from classic unit to full integration tests.
To be sure none of the options in Spring MVC Test are classic unit tests but they are a
little closer to it. For example you can isolate the service layer with mocks
injected into controllers and then you're testing the web layer only through
the `DispatcherServlet` and with actual Spring configuration, just like you might test
the database layer in isolation of the layers above. Or you could be using the
standalone setup focusing on one controller at a time and manually providing the
configuration required to make it work.
There are pros and cons for each approach. The options provided in __Spring MVC Test__
are different stops on the scale from classic unit testing to full integration testing.
To be certain, none of the options in Spring MVC Test fall under the category of classic
unit testing, but they _are_ a little closer to it. For example, you can isolate the web
layer by injecting mocked services into controllers, in which case you're testing the web
layer only through the `DispatcherServlet` but with actual Spring configuration, just
like you might test the data access layer in isolation from the layers above. Or you
can use the standalone setup focusing on one controller at a time and manually providing
the configuration required to make it work.
Another important distinction when using __Spring MVC Test__ is that conceptually such
tests are on the inside of the server-side so you can check what handler was used,
tests are on the _inside_ of the server-side so you can check what handler was used,
if an exception was handled with a HandlerExceptionResolver, what the content of the
model is, what binding errors there were, etc. That means it's easier to write
expectations since the server is not a black box as it is when testing it through
an actual HTTP client. This is generally the advantage of classic unit testing that it's
an actual HTTP client. This is generally an advantage of classic unit testing, that it's
easier to write, reason about, and debug but does not replace the need for full
integration tests. At the same time it's important not to lose sight of the fact
the response is the most important thing to check. In short there is room here for
multiple styles and strategies of testing even in the same project.
integration tests. At the same time it's important not to lose sight of the fact that
the response is the most important thing to check. In short, there is room here for
multiple styles and strategies of testing even within the same project.
[[spring-mvc-test-server-resources]]

Loading…
Cancel
Save