Document WebApplicationContext support in the TCF
This commit adds documentation for the following new features in the Spring TestContext Framework within the Testing chapter of the reference manual. - @WebAppConfiguration and context caching - WebDelegatingSmartContextLoader - AnnotationConfigWebContextLoader - GenericXmlWebContextLoader - Loading a WebApplicationContext in integration tests - ServletTestExecutionListener - Testing request and session scoped beans Issue: SPR-9864
This commit is contained in:
parent
7f1b990ee9
commit
a1053d4364
|
|
@ -265,15 +265,16 @@
|
|||
<title>Context management and caching</title>
|
||||
|
||||
<para>The Spring TestContext Framework provides consistent loading of
|
||||
Spring <classname>ApplicationContext</classname>s and caching of those
|
||||
contexts. Support for the caching of loaded contexts is important,
|
||||
because startup time can become an issue — not because of the overhead
|
||||
of Spring itself, but because the objects instantiated by the Spring
|
||||
container take time to instantiate. For example, a project with 50 to
|
||||
100 Hibernate mapping files might take 10 to 20 seconds to load the
|
||||
mapping files, and incurring that cost before running every test in
|
||||
every test fixture leads to slower overall test runs that reduce
|
||||
developer productivity.</para>
|
||||
Spring <classname>ApplicationContext</classname>s and
|
||||
<classname>WebApplicationContext</classname>s as well as caching of
|
||||
those contexts. Support for the caching of loaded contexts is
|
||||
important, because startup time can become an issue — not because of
|
||||
the overhead of Spring itself, but because the objects instantiated by
|
||||
the Spring container take time to instantiate. For example, a project
|
||||
with 50 to 100 Hibernate mapping files might take 10 to 20 seconds to
|
||||
load the mapping files, and incurring that cost before running every
|
||||
test in every test fixture leads to slower overall test runs that
|
||||
reduce developer productivity.</para>
|
||||
|
||||
<para>Test classes typically declare either an array of
|
||||
<emphasis>resource locations</emphasis> for XML configuration metadata
|
||||
|
|
@ -526,6 +527,55 @@ public class CustomLoaderXmlApplicationContextTests {
|
|||
details.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><emphasis
|
||||
role="bold"><interfacename>@WebAppConfiguration</interfacename></emphasis></para>
|
||||
|
||||
<para>A class-level annotation that is used to declare that the
|
||||
<interfacename>ApplicationContext</interfacename> loaded for an
|
||||
integration test should be a
|
||||
<interfacename>WebApplicationContext</interfacename>. The mere
|
||||
presence of <interfacename>@WebAppConfiguration</interfacename> on
|
||||
a test class ensures that a
|
||||
<interfacename>WebApplicationContext</interfacename> will be
|
||||
loaded for the test, using the default value of
|
||||
<literal>"file:src/main/webapp"</literal> for the path to the root
|
||||
of the web application (i.e., the <emphasis>resource base
|
||||
path</emphasis>). The resource base path is used behind the scenes
|
||||
to create a <classname>MockServletContext</classname> which serves
|
||||
as the <interfacename>ServletContext</interfacename> for the
|
||||
test's
|
||||
<interfacename>WebApplicationContext</interfacename>.</para>
|
||||
|
||||
<programlisting language="java">@ContextConfiguration
|
||||
<emphasis role="bold">@WebAppConfiguration</emphasis>
|
||||
public class WebAppTests {
|
||||
<lineannotation>// class body...</lineannotation>
|
||||
}</programlisting>
|
||||
|
||||
<para>To override the default, specify a different base resource
|
||||
path via the <emphasis>implicit</emphasis>
|
||||
<interfacename>value</interfacename> attribute. Both
|
||||
<literal>classpath:</literal> and <literal>file:</literal>
|
||||
resource prefixes are supported. If no resource prefix is supplied
|
||||
the path is assumed to be a file system resource.</para>
|
||||
|
||||
<programlisting language="java">@ContextConfiguration
|
||||
<emphasis role="bold">@WebAppConfiguration("classpath:test-web-resources")</emphasis>
|
||||
public class WebAppTests {
|
||||
<lineannotation>// class body...</lineannotation>
|
||||
}</programlisting>
|
||||
|
||||
<para>Note that
|
||||
<interfacename>@WebAppConfiguration</interfacename> must be used
|
||||
in conjunction with
|
||||
<interfacename>@ContextConfiguration</interfacename>, either
|
||||
within a single test class or within a test class hierarchy. See
|
||||
the Javadoc for
|
||||
<interfacename>@WebAppConfiguration</interfacename> for further
|
||||
details.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><emphasis role="bold">
|
||||
<interfacename>@ActiveProfiles</interfacename> </emphasis></para>
|
||||
|
|
@ -1107,14 +1157,16 @@ public void testProcessRepeatedly() {
|
|||
<classname>TestContextManager</classname> with which the listener
|
||||
is registered.</para>
|
||||
|
||||
<para>Spring provides three
|
||||
<para>Spring provides four
|
||||
<interfacename>TestExecutionListener</interfacename>
|
||||
implementations that are configured by default:
|
||||
<classname>ServletTestExecutionListener</classname>,
|
||||
<classname>DependencyInjectionTestExecutionListener</classname>,
|
||||
<classname>DirtiesContextTestExecutionListener</classname>, and
|
||||
<classname>TransactionalTestExecutionListener</classname>.
|
||||
Respectively, they support dependency injection of the test
|
||||
instance, handling of the
|
||||
Respectively, they support Servlet API mocks for a
|
||||
<interfacename>WebApplicationContext</interfacename>, dependency
|
||||
injection of the test instance, handling of the
|
||||
<interfacename>@DirtiesContext</interfacename> annotation, and
|
||||
transactional test execution with default rollback
|
||||
semantics.</para>
|
||||
|
|
@ -1142,18 +1194,18 @@ public void testProcessRepeatedly() {
|
|||
supersedes the <interfacename>ContextLoader</interfacename> SPI
|
||||
that was introduced in Spring 2.5. Specifically, a
|
||||
<interfacename>SmartContextLoader</interfacename> can choose to
|
||||
process either resource <varname>locations</varname> or annotated
|
||||
<varname>classes</varname>. Furthermore, a
|
||||
process resource <varname>locations</varname>, annotated
|
||||
<varname>classes</varname>, or context
|
||||
<varname>initializers</varname>. Furthermore, a
|
||||
<interfacename>SmartContextLoader</interfacename> can set active
|
||||
bean definition profiles in the context that it loads.</para>
|
||||
|
||||
<para>Spring provides the following out-of-the-box
|
||||
implementations:</para>
|
||||
<para>Spring provides the following implementations:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><classname>DelegatingSmartContextLoader</classname>: the
|
||||
default loader which delegates internally to an
|
||||
<para><classname>DelegatingSmartContextLoader</classname>: one
|
||||
of two default loaders which delegates internally to an
|
||||
<classname>AnnotationConfigContextLoader</classname> or a
|
||||
<classname>GenericXmlContextLoader</classname> depending
|
||||
either on the configuration declared for the test class or on
|
||||
|
|
@ -1162,21 +1214,48 @@ public void testProcessRepeatedly() {
|
|||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>AnnotationConfigContextLoader</classname>:
|
||||
loads an application context from <emphasis>annotated
|
||||
classes</emphasis>.</para>
|
||||
<para><classname>WebDelegatingSmartContextLoader</classname>:
|
||||
one of two default loaders which delegates internally to an
|
||||
<classname>AnnotationConfigWebContextLoader</classname> or a
|
||||
<classname>GenericXmlWebContextLoader</classname> depending
|
||||
either on the configuration declared for the test class or on
|
||||
the presence of default locations or default configuration
|
||||
classes. A web <interfacename>ContextLoader</interfacename>
|
||||
will only be used if
|
||||
<interfacename>@WebAppConfiguration</interfacename> is present
|
||||
on the test class.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>GenericXmlContextLoader</classname>: loads an
|
||||
application context from XML <emphasis>resource
|
||||
locations</emphasis>.</para>
|
||||
<para><classname>AnnotationConfigContextLoader</classname>:
|
||||
loads a standard
|
||||
<interfacename>ApplicationContext</interfacename> from
|
||||
<emphasis>annotated classes</emphasis>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>AnnotationConfigWebContextLoader</classname>:
|
||||
loads a <interfacename>WebApplicationContext</interfacename>
|
||||
from <emphasis>annotated classes</emphasis>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>GenericXmlContextLoader</classname>: loads a
|
||||
standard <interfacename>ApplicationContext</interfacename>
|
||||
from XML <emphasis>resource locations</emphasis>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>GenericXmlWebContextLoader</classname>: loads
|
||||
a <interfacename>WebApplicationContext</interfacename> from
|
||||
XML <emphasis>resource locations</emphasis>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>GenericPropertiesContextLoader</classname>:
|
||||
loads an application context from Java Properties
|
||||
files.</para>
|
||||
loads a standard
|
||||
<interfacename>ApplicationContext</interfacename> from Java
|
||||
Properties files.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</listitem>
|
||||
|
|
@ -1194,8 +1273,8 @@ public void testProcessRepeatedly() {
|
|||
<para>Each <classname>TestContext</classname> provides context
|
||||
management and caching support for the test instance it is responsible
|
||||
for. Test instances do not automatically receive access to the
|
||||
configured <classname>ApplicationContext</classname>. However, if a
|
||||
test class implements the
|
||||
configured <interfacename>ApplicationContext</interfacename>. However,
|
||||
if a test class implements the
|
||||
<interfacename>ApplicationContextAware</interfacename> interface, a
|
||||
reference to the <classname>ApplicationContext</classname> is supplied
|
||||
to the test instance. Note that
|
||||
|
|
@ -1203,7 +1282,7 @@ public void testProcessRepeatedly() {
|
|||
<classname>AbstractTestNGSpringContextTests</classname> implement
|
||||
<interfacename>ApplicationContextAware</interfacename> and therefore
|
||||
provide access to the <classname>ApplicationContext</classname>
|
||||
out-of-the-box.</para>
|
||||
automatically.</para>
|
||||
|
||||
<tip>
|
||||
<title>@Autowired ApplicationContext</title>
|
||||
|
|
@ -1221,6 +1300,21 @@ public class MyTest {
|
|||
<emphasis role="bold">@Autowired</emphasis>
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
<lineannotation>// class body...</lineannotation>
|
||||
}</programlisting>
|
||||
|
||||
<para>Similarly, if your test is configured to load a
|
||||
<interfacename>WebApplicationContext</interfacename>, you can inject
|
||||
the web application context into your test as follows:</para>
|
||||
|
||||
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
|
||||
<emphasis role="bold">@WebAppConfiguration</emphasis>
|
||||
@ContextConfiguration
|
||||
public class MyWebAppTest {
|
||||
|
||||
<emphasis role="bold">@Autowired</emphasis>
|
||||
private WebApplicationContext wac;
|
||||
|
||||
<lineannotation>// class body...</lineannotation>
|
||||
}</programlisting>
|
||||
|
||||
|
|
@ -1781,12 +1875,207 @@ public class TransferServiceTest {
|
|||
<!-- TODO Consider documenting inheritance for active profiles. -->
|
||||
</section>
|
||||
|
||||
<section xml:id="testcontext-ctx-management-web">
|
||||
<title>Loading a WebApplicationContext</title>
|
||||
|
||||
<para>Spring 3.2 introduces support for loading a
|
||||
<interfacename>WebApplicationContext</interfacename> in integration
|
||||
tests. To instruct the TestContext framework to load a
|
||||
<interfacename>WebApplicationContext</interfacename> instead of a
|
||||
standard <interfacename>ApplicationContext</interfacename>, simply
|
||||
annotate the respective test class with
|
||||
<interfacename>@WebAppConfiguration</interfacename>.</para>
|
||||
|
||||
<para>The presence of
|
||||
<interfacename>@WebAppConfiguration</interfacename> on your test
|
||||
class instructs the TestContext framework (TCF) that a
|
||||
<interfacename>WebApplicationContext</interfacename> (WAC) should be
|
||||
loaded for your integration tests. In the background the TCF makes
|
||||
sure that a <interfacename>MockServletContext</interfacename> is
|
||||
created and supplied to your test's WAC. By default the base
|
||||
resource path for your
|
||||
<interfacename>MockServletContext</interfacename> will be set to
|
||||
<emphasis>"src/main/webapp"</emphasis>. This is interpreted as a
|
||||
path relative to the root of your JVM (i.e., normally the path to
|
||||
your project). If you're familiar with the directory structure of a
|
||||
web application in a Maven project, you'll know that
|
||||
<emphasis>"src/main/webapp"</emphasis> is the default location for
|
||||
the root of your WAR. If you need to override this default, simply
|
||||
provide an alternate path to the
|
||||
<interfacename>@WebAppConfiguration</interfacename> annotation
|
||||
(e.g.,
|
||||
<interfacename>@WebAppConfiguration("src/test/webapp")</interfacename>).
|
||||
If you wish to reference a base resource path from the classpath
|
||||
instead of the file system, just use Spring's
|
||||
<emphasis>classpath:</emphasis> prefix.</para>
|
||||
|
||||
<para>Please note that Spring's testing support for
|
||||
<interfacename>WebApplicationContexts</interfacename> is on par with
|
||||
its support for standard
|
||||
<interfacename>ApplicationContexts</interfacename>. When testing
|
||||
with a <interfacename>WebApplicationContext</interfacename> you are
|
||||
free to declare either XML configuration files or
|
||||
<interfacename>@Configuration</interfacename> classes via
|
||||
<interfacename>@ContextConfiguration</interfacename>. You are of
|
||||
course also free to use any other test annotations such as
|
||||
<interfacename>@TestExecutionListeners</interfacename>,
|
||||
<interfacename>@TransactionConfiguration</interfacename>,
|
||||
<interfacename>@ActiveProfiles</interfacename>, etc.</para>
|
||||
|
||||
<para>The following examples demonstrate some of the various
|
||||
configuration options for loading a
|
||||
<interfacename>WebApplicationContext</interfacename>.</para>
|
||||
|
||||
<example>
|
||||
<title>Conventions</title>
|
||||
|
||||
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
|
||||
|
||||
// defaults to "file:src/main/webapp"
|
||||
@WebAppConfiguration
|
||||
|
||||
// detects "WacTests-context.xml" in same package
|
||||
// or static nested @Configuration class
|
||||
@ContextConfiguration
|
||||
|
||||
public class WacTests {
|
||||
//...
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>The above example demonstrates the TestContext framework's
|
||||
support for <emphasis>convention over configuration</emphasis>. If
|
||||
you annotate a test class with
|
||||
<interfacename>@WebAppConfiguration</interfacename> without
|
||||
specifying a resource base path, the resource path will effectively
|
||||
default to <emphasis>"file:src/main/webapp"</emphasis>. Similarly,
|
||||
if you declare <interfacename>@ContextConfiguration</interfacename>
|
||||
without specifying resource
|
||||
<interfacename>locations</interfacename>, annotated
|
||||
<interfacename>classes</interfacename>, or context
|
||||
<interfacename>initializers</interfacename>, Spring will attempt to
|
||||
detect the presence of your configuration using conventions (i.e.,
|
||||
<emphasis>"WacTests-context.xml"</emphasis> in the same package as
|
||||
the <interfacename>WacTests</interfacename> class or static nested
|
||||
<interfacename>@Configuration</interfacename> classes).</para>
|
||||
|
||||
<example>
|
||||
<title>Default Resource Semantics</title>
|
||||
|
||||
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
|
||||
|
||||
// file system resource
|
||||
@WebAppConfiguration("webapp")
|
||||
|
||||
// classpath resource
|
||||
@ContextConfiguration("/spring/test-servlet-config.xml")
|
||||
|
||||
public class WacTests {
|
||||
//...
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>This example demonstrates how to explicitly declare a resource
|
||||
base path with <interfacename>@WebAppConfiguration</interfacename>
|
||||
and an XML resource location with
|
||||
<interfacename>@ContextConfiguration</interfacename>. The important
|
||||
thing to note here is the different semantics for paths with these
|
||||
two annotations. By default,
|
||||
<interfacename>@WebAppConfiguration</interfacename> resource paths
|
||||
are file system based; whereas,
|
||||
<interfacename>@ContextConfiguration</interfacename> resource
|
||||
locations are classpath based.</para>
|
||||
|
||||
<example>
|
||||
<title>Explicit Resource Semantics</title>
|
||||
|
||||
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
|
||||
|
||||
// classpath resource
|
||||
@WebAppConfiguration("classpath:test-web-resources")
|
||||
|
||||
// file system resource
|
||||
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
|
||||
|
||||
public class WacTests {
|
||||
//...
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>In this third example, we see that we can override the default
|
||||
resource semantics for both annotations by specifying a Spring
|
||||
resource prefix. Contrast the comments in this example with the
|
||||
previous example.</para>
|
||||
|
||||
<section xml:id="testcontext-ctx-management-web-mocks">
|
||||
<title>Working with Web Mocks</title>
|
||||
|
||||
<para>To provide comprehensive web testing support, Spring 3.2
|
||||
introduces a new
|
||||
<interfacename>ServletTestExecutionListener</interfacename> that
|
||||
is enabled by default. When testing against a
|
||||
<interfacename>WebApplicationContext</interfacename> this <link
|
||||
linkend="testcontext-key-abstractions">TestExecutionListener</link>
|
||||
sets up default thread-local state via Spring Web's
|
||||
<interfacename>RequestContextHolder</interfacename> before each
|
||||
test method and creates a
|
||||
<interfacename>MockHttpServletRequest</interfacename>,
|
||||
<interfacename>MockHttpServletResponse</interfacename>, and
|
||||
<interfacename>ServletWebRequest</interfacename> based on the base
|
||||
resource path configured via
|
||||
<interfacename>@WebAppConfiguration</interfacename>.
|
||||
<interfacename>ServletTestExecutionListener</interfacename> also
|
||||
ensures that the
|
||||
<interfacename>MockHttpServletResponse</interfacename> and
|
||||
<interfacename>ServletWebRequest</interfacename> can be injected
|
||||
into the test instance, and once the test is complete it cleans up
|
||||
thread-local state.</para>
|
||||
|
||||
<para>Once you have a
|
||||
<interfacename>WebApplicationContext</interfacename> loaded for
|
||||
your test you might find that you need to interact with the web
|
||||
mocks — for example, to set up your test fixture or to perform
|
||||
assertions after invoking your web component. The following
|
||||
example demonstrates which mocks can be autowired into your test
|
||||
instance. Note that the
|
||||
<interfacename>WebApplicationContext</interfacename> and
|
||||
<interfacename>MockServletContext</interfacename> are both cached
|
||||
across the test suite; whereas, the other mocks are managed per
|
||||
test method by the
|
||||
<interfacename>ServletTestExecutionListener</interfacename>.</para>
|
||||
|
||||
<example>
|
||||
<title>Injecting Mocks</title>
|
||||
|
||||
<programlisting language="java">@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class WacTests {
|
||||
|
||||
@Autowired WebApplicationContext wac; // cached
|
||||
|
||||
@Autowired MockServletContext servletContext; // cached
|
||||
|
||||
@Autowired MockHttpSession session;
|
||||
|
||||
@Autowired MockHttpServletRequest request;
|
||||
|
||||
@Autowired MockHttpServletResponse response;
|
||||
|
||||
@Autowired ServletWebRequest webRequest;
|
||||
|
||||
//...
|
||||
}</programlisting>
|
||||
</example>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section xml:id="testcontext-ctx-management-caching">
|
||||
<title>Context caching</title>
|
||||
|
||||
<para>Once the TestContext framework loads an
|
||||
<interfacename>ApplicationContext</interfacename> for a test, that
|
||||
context will be cached and reused for <emphasis
|
||||
<interfacename>ApplicationContext</interfacename> (or
|
||||
<interfacename>WebApplicationContext</interfacename>) for a test,
|
||||
that context will be cached and reused for <emphasis
|
||||
role="bold">all</emphasis> subsequent tests that declare the same
|
||||
unique context configuration within the same test suite. To
|
||||
understand how caching works, it is important to understand what is
|
||||
|
|
@ -1826,6 +2115,11 @@ public class TransferServiceTest {
|
|||
<para><varname>activeProfiles</varname> <emphasis>(from
|
||||
@ActiveProfiles)</emphasis></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><varname>resourceBasePath</varname> <emphasis>(from
|
||||
@WebAppConfiguration)</emphasis></para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>For example, if <classname>TestClassA</classname> specifies
|
||||
|
|
@ -1838,7 +2132,8 @@ public class TransferServiceTest {
|
|||
solely on those locations. So if <classname>TestClassB</classname>
|
||||
also defines <literal>{"app-config.xml",
|
||||
"test-config.xml"}</literal> for its locations (either explicitly or
|
||||
implicitly through inheritance) and does not define a different
|
||||
implicitly through inheritance) but does not define
|
||||
<interfacename>@WebAppConfiguration</interfacename>, a different
|
||||
<interfacename>ContextLoader</interfacename>, different active
|
||||
profiles, or different context initializers, then the same
|
||||
<interfacename>ApplicationContext</interfacename> will be shared by
|
||||
|
|
@ -2055,6 +2350,170 @@ public class HibernateTitleRepositoryTests {
|
|||
</note>
|
||||
</section>
|
||||
|
||||
<section xml:id="testcontext-web-scoped-beans">
|
||||
<title>Testing request and session scoped beans</title>
|
||||
|
||||
<para><link linkend="beans-factory-scopes-other">Request and session
|
||||
scoped beans</link> have been supported by Spring for several years
|
||||
now, but it's always been a bit non-trivial to test them. As of Spring
|
||||
3.2 it's now a breeze to test your request-scoped and session-scoped
|
||||
beans by following these steps.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Ensure that a
|
||||
<interfacename>WebApplicationContext</interfacename> is loaded for
|
||||
your test by annotating your test class with
|
||||
<interfacename>@WebAppConfiguration</interfacename>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Inject the mock request or session into your test instance
|
||||
and prepare your test fixture as appropriate.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Invoke your web component that you retrieved from the
|
||||
configured <interfacename>WebApplicationContext</interfacename>
|
||||
(i.e., via dependency injection).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Perform assertions against the mocks.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The following code snippet displays the XML configuration for a
|
||||
login use case. Note that the <literal>userService</literal> bean has
|
||||
a dependency on a request-scoped <literal>loginAction</literal> bean.
|
||||
Also, the <classname>LoginAction</classname> is instantiated using
|
||||
<link linkend="expressions">SpEL expressions</link> that retrieve the
|
||||
username and password from the current HTTP request. In our test, we
|
||||
will want to configure these request parameters via the mock managed
|
||||
by the TestContext framework.</para>
|
||||
|
||||
<example>
|
||||
<title>Request-scoped bean configuration</title>
|
||||
|
||||
<programlisting language="xml"><beans>
|
||||
|
||||
<bean id="userService"
|
||||
class="com.example.SimpleUserService"
|
||||
c:loginAction-ref="loginAction" />
|
||||
|
||||
<bean id="loginAction" class="com.example.LoginAction"
|
||||
c:username="#{request.getParameter('user')}"
|
||||
c:password="#{request.getParameter('pswd')}"
|
||||
scope="request">
|
||||
<aop:scoped-proxy />
|
||||
</bean>
|
||||
|
||||
</beans></programlisting>
|
||||
</example>
|
||||
|
||||
<para>In <classname>RequestScopedBeanTests</classname> we inject both
|
||||
the <classname>UserService</classname> (i.e., the subject under test)
|
||||
and the <classname>MockHttpServletRequest</classname> into our test
|
||||
instance. Within our <function>requestScope()</function> test method
|
||||
we set up our test fixture by setting request parameters in the
|
||||
provided <classname>MockHttpServletRequest</classname>. When the
|
||||
<function>loginUser()</function> method is invoked on our
|
||||
<literal>userService</literal> we are assured that the user service
|
||||
has access to the request-scoped <literal>loginAction</literal> for
|
||||
the current <classname>MockHttpServletRequest</classname> (i.e., the
|
||||
one we just set parameters in). We can then perform assertions against
|
||||
the results based on the known inputs for the username and
|
||||
password.</para>
|
||||
|
||||
<example>
|
||||
<title>Request-scoped bean test</title>
|
||||
|
||||
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class RequestScopedBeanTests {
|
||||
|
||||
@Autowired UserService userService;
|
||||
@Autowired MockHttpServletRequest request;
|
||||
|
||||
@Test
|
||||
public void requestScope() {
|
||||
|
||||
request.setParameter("user", "enigma");
|
||||
request.setParameter("pswd", "$pr!ng");
|
||||
|
||||
LoginResults results = userService.loginUser();
|
||||
|
||||
// assert results
|
||||
}
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>The following code snippet is similar to the one we saw above
|
||||
for a request-scoped bean; however, this time the
|
||||
<literal>userService</literal> bean has a dependency on a
|
||||
session-scoped <literal>userPreferences</literal> bean. Note that the
|
||||
<classname>UserPreferences</classname> bean is instantiated using a
|
||||
SpEL expression that retrieves the <emphasis>theme</emphasis> from the
|
||||
current HTTP session. In our test, we will need to configure a theme
|
||||
in the mock session managed by the TestContext framework.</para>
|
||||
|
||||
<example>
|
||||
<title>Session-scoped bean configuration</title>
|
||||
|
||||
<programlisting language="xml"><beans>
|
||||
|
||||
<bean id="userService"
|
||||
class="com.example.SimpleUserService"
|
||||
c:userPreferences-ref="userPreferences" />
|
||||
|
||||
<bean id="userPreferences"
|
||||
class="com.example.UserPreferences"
|
||||
c:theme="#{session.getAttribute('theme')}"
|
||||
scope="session">
|
||||
<aop:scoped-proxy />
|
||||
</bean>
|
||||
|
||||
</beans></programlisting>
|
||||
</example>
|
||||
|
||||
<para>In <classname>SessionScopedBeanTests</classname> we inject the
|
||||
<classname>UserService</classname> and the
|
||||
<classname>MockHttpSession</classname> into our test instance. Within
|
||||
our <function>sessionScope()</function> test method we set up our test
|
||||
fixture by setting the expected "theme" attribute in the provided
|
||||
<classname>MockHttpSession</classname>. When the
|
||||
<function>processUserPreferences()</function> method is invoked on our
|
||||
<literal>userService</literal> we are assured that the user service
|
||||
has access to the session-scoped <literal>userPreferences</literal>
|
||||
for the current <classname>MockHttpSession</classname>, and we can
|
||||
perform assertions against the results based on the configured
|
||||
theme.</para>
|
||||
|
||||
<example>
|
||||
<title>Session-scoped bean test</title>
|
||||
|
||||
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class SessionScopedBeanTests {
|
||||
|
||||
@Autowired UserService userService;
|
||||
@Autowired MockHttpSession session;
|
||||
|
||||
@Test
|
||||
public void sessionScope() throws Exception {
|
||||
|
||||
session.setAttribute("theme", "blue");
|
||||
|
||||
Results results = userService.processUserPreferences();
|
||||
|
||||
// assert results
|
||||
}
|
||||
}</programlisting>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
<section xml:id="testcontext-tx">
|
||||
<title>Transaction management</title>
|
||||
|
||||
|
|
@ -2420,10 +2879,10 @@ public class SimpleTest {
|
|||
<para>The <filename>spring-test</filename> module uses a different
|
||||
package <classname>org.springframework.test.web</classname> but
|
||||
otherwise is nearly identical with two exceptions. One is support for
|
||||
features new in 3.2 (e.g. async web requests). The other relates to
|
||||
the options for creating a <classname>MockMvc</classname> instance. In
|
||||
Spring Framework 3.2, this can only be done through the TestContext
|
||||
framework, which provides caching benefits for the loaded
|
||||
features new in 3.2 (e.g. asynchronous web requests). The other
|
||||
relates to the options for creating a <classname>MockMvc</classname>
|
||||
instance. In Spring Framework 3.2, this can only be done through the
|
||||
TestContext framework, which provides caching benefits for the loaded
|
||||
configuration.</para>
|
||||
</sidebar>
|
||||
|
||||
|
|
@ -2432,12 +2891,13 @@ public class SimpleTest {
|
|||
through a fluent API. Typically it loads the actual Spring configuration
|
||||
through the <emphasis>TestContext framework</emphasis> and always uses
|
||||
the <classname>DispatcherServlet</classname> to process requests thus
|
||||
approximating full integration tests without requiring a running servlet
|
||||
approximating full integration tests without requiring a running Servlet
|
||||
container.</para>
|
||||
|
||||
<para>Client-side tests are <classname>RestTemplate</classname>-based
|
||||
and allow tests for code that relies on the <code>RestTemplate</code>
|
||||
without requiring a running server to respond to the requests.</para>
|
||||
and allow tests for code that relies on the
|
||||
<classname>RestTemplate</classname> without requiring a running server
|
||||
to respond to the requests.</para>
|
||||
|
||||
<section xml:id="spring-mvc-test-server">
|
||||
<title>Server-Side Tests</title>
|
||||
|
|
@ -2476,8 +2936,8 @@ public class SimpleTest {
|
|||
<classname>MockHttpServletResponse</classname> works, you'll know that
|
||||
forwards and redirects are not actually executed. Instead "forwarded"
|
||||
and "redirected" URLs are saved and can be asserted in tests. This
|
||||
means if you are using JSPs, you can verify the JSP page to which the request was
|
||||
forwarded.</para>
|
||||
means if you are using JSPs, you can verify the JSP page to which the
|
||||
request was forwarded.</para>
|
||||
|
||||
<para>All other means of rendering including
|
||||
<interfacename>@ResponseBody</interfacename> methods and
|
||||
|
|
@ -2520,10 +2980,10 @@ public class ExampleTests {
|
|||
<para>The test relies on the
|
||||
<interfacename>WebApplicationContext</interfacename> support of the
|
||||
<emphasis>TestContext framework</emphasis>. It loads Spring
|
||||
configuration from an XML config file located in the same package as
|
||||
the test class (also supports Java config) and injects the created
|
||||
<interfacename>WebApplicationContext</interfacename> into the test so
|
||||
a <classname>MockMvc</classname> instance can be created with
|
||||
configuration from an XML configuration file located in the same
|
||||
package as the test class (also supports JavaConfig) and injects the
|
||||
created <interfacename>WebApplicationContext</interfacename> into the
|
||||
test so a <classname>MockMvc</classname> instance can be created with
|
||||
it.</para>
|
||||
|
||||
<para>The <classname>MockMvc</classname> is then used to perform a
|
||||
|
|
@ -2589,7 +3049,7 @@ public class MyWebTests {
|
|||
without loading any Spring configuration. Instead basic Spring MVC
|
||||
configuration suitable for testing annotated controllers is
|
||||
automatically created. The created configuration is comparable to
|
||||
that of the MVC Java config (and the MVC namespace) and can be
|
||||
that of the MVC JavaConfig (and the MVC namespace) and can be
|
||||
customized to a degree through builder-style methods:</para>
|
||||
|
||||
<programlisting language="java">public class MyWebTests {
|
||||
|
|
@ -2616,8 +3076,7 @@ public class MyWebTests {
|
|||
remain focused on testing the web layer. Here is an example of
|
||||
declaring a mock service with Mockito:</para>
|
||||
|
||||
<programlisting language="xml">
|
||||
<bean id="accountService" class="org.mockito.Mockito" factory-method="mock">
|
||||
<programlisting language="xml"><bean id="accountService" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg value="org.example.AccountService"/>
|
||||
</bean>
|
||||
</programlisting>
|
||||
|
|
@ -2667,45 +3126,42 @@ public class AccountTests {
|
|||
additional builder-style methods corresponding to properties of
|
||||
<classname>MockHttpServletRequest</classname>. For example:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
|
||||
<programlisting language="java">mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
|
||||
</programlisting>
|
||||
|
||||
<para>In addition to all the HTTP methods, you can also perform file
|
||||
upload requests, which internally creates an instance of
|
||||
<classname>MockMultipartHttpServletRequest</classname>:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8")));
|
||||
<programlisting language="java">mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8")));
|
||||
</programlisting>
|
||||
|
||||
<para>Query string parameters can be specified in the URI
|
||||
template:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
mockMvc.perform(get("/hotels?foo={foo}", "bar"));
|
||||
<programlisting language="java">mockMvc.perform(get("/hotels?foo={foo}", "bar"));
|
||||
</programlisting>
|
||||
|
||||
<para>Or by adding Servlet request parameters:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
mockMvc.perform(get("/hotels").param("foo", "bar"));
|
||||
<programlisting language="java">mockMvc.perform(get("/hotels").param("foo", "bar"));
|
||||
</programlisting>
|
||||
|
||||
<para>If application code relies on Servlet request parameters, and
|
||||
doesn't check the query string, as is most often the case, then it
|
||||
doesn't matter how parameters are added. Keep in mind though that
|
||||
parameters provided in the URI template will be decoded while
|
||||
parameters provided through the <code>param(...)</code> method are
|
||||
expected to be decoded.</para>
|
||||
parameters provided through the <function>param(...)</function>
|
||||
method are expected to be decoded.</para>
|
||||
|
||||
<para>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 contextPath and servletPath
|
||||
accordingly so that request mappings will work:</para>
|
||||
and the Servlet path from the request URI. If you must test with the
|
||||
full request URI, be sure to set the
|
||||
<function>contextPath</function> and
|
||||
<function>servletPath</function> accordingly so that request
|
||||
mappings will work:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
|
||||
<programlisting language="java">mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
|
||||
</programlisting>
|
||||
|
||||
<para>Looking at the above example, it would be cumbersome to set
|
||||
|
|
@ -2713,8 +3169,7 @@ mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/m
|
|||
why you can define default request properties when building the
|
||||
<classname>MockMvc</classname>:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
public class MyWebTests {
|
||||
<programlisting language="java">public class MyWebTests {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
|
|
@ -2740,14 +3195,13 @@ public class MyWebTests {
|
|||
<title>Defining Expectations</title>
|
||||
|
||||
<para>Expectations can be defined by appending one or more
|
||||
<code>.andExpect(..)</code> after call to perform the
|
||||
<function>.andExpect(..)</function> after call to perform the
|
||||
request:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
|
||||
<programlisting language="java">mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
|
||||
</programlisting>
|
||||
|
||||
<para><code>MockMvcResultMatchers.*</code> defines a number of
|
||||
<para><literal>MockMvcResultMatchers.*</literal> defines a number of
|
||||
static members, some of which return types with additional methods,
|
||||
for asserting the result of the performed request. The assertions
|
||||
fall in two general categories.</para>
|
||||
|
|
@ -2772,41 +3226,35 @@ mockMvc.perform(post("/persons"))
|
|||
</programlisting>
|
||||
|
||||
<para>Many times when writing tests, it's useful to dump the result
|
||||
of the performed request. This can be done as follows:</para>
|
||||
of the performed request. This can be done as follows, where
|
||||
<function>print()</function> is a static import from
|
||||
<classname>MockMvcResultHandlers</classname>:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
mockMvc.perform(post("/persons"))
|
||||
<programlisting language="java">mockMvc.perform(post("/persons"))
|
||||
.andDo(print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(model().attributeHasErrors("person"));
|
||||
</programlisting>
|
||||
.andExpect(model().attributeHasErrors("person"));</programlisting>
|
||||
|
||||
<para>where <code>print()</code> is a static import from
|
||||
<code>MockMvcResultHandlers</code>. As long as request processing
|
||||
does cause an unhandled exception, the <code>print()</code> method
|
||||
will print all the available result data to
|
||||
<code>System.out</code>.</para>
|
||||
<para>As long as request processing causes an unhandled exception,
|
||||
the <function>print()</function> method will print all the available
|
||||
result data to <literal>System.out</literal>.</para>
|
||||
|
||||
<para>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 <code>.andReturn()</code> at the end after all
|
||||
expectations:</para>
|
||||
done by appending <function>.andReturn()</function> at the end after
|
||||
all expectations:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn();
|
||||
// ...
|
||||
</programlisting>
|
||||
<programlisting language="java">MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn();
|
||||
// ...</programlisting>
|
||||
|
||||
<para>When all tests repeat the same expectations, you can define
|
||||
the common expectations once when building the
|
||||
<classname>MockMvc</classname>:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
standaloneSetup(new SimpleController())
|
||||
<programlisting language="java">standaloneSetup(new SimpleController())
|
||||
.alwaysExpect(status().isOk())
|
||||
.alwaysExpect(content().contentType("application/json;charset=UTF-8"))
|
||||
.build()
|
||||
</programlisting>
|
||||
.build()</programlisting>
|
||||
|
||||
<para>Note that the expectation is <emphasis>always</emphasis>
|
||||
applied and cannot be overridden without creating a separate
|
||||
|
|
@ -2817,21 +3265,17 @@ standaloneSetup(new SimpleController())
|
|||
xl:href="https://github.com/SpringSource/spring-hateoas">Spring
|
||||
HATEOAS</link>, the resulting links can be verified:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people"));
|
||||
</programlisting>
|
||||
<programlisting language="java">mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people"));</programlisting>
|
||||
|
||||
<para>When XML response content contains hypermedia links created
|
||||
with <link
|
||||
xl:href="https://github.com/SpringSource/spring-hateoas">Spring
|
||||
HATEOAS</link>, the resulting links can be verified:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
Map<String, String> ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom");
|
||||
<programlisting language="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"));
|
||||
</programlisting>
|
||||
.andExpect(xpath("/person/ns:link[@rel='self']/@href", ns).string("http://localhost:8080/people"));</programlisting>
|
||||
</section>
|
||||
|
||||
<section xml:id="spring-mvc-test-server-filters">
|
||||
|
|
@ -2841,9 +3285,7 @@ mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML))
|
|||
register one or more <interfacename>Filter</interfacename>
|
||||
instances:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();
|
||||
</programlisting>
|
||||
<programlisting language="java">mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();</programlisting>
|
||||
|
||||
<para>Registered filters will be invoked through
|
||||
<classname>MockFilterChain</classname> from
|
||||
|
|
@ -2870,16 +3312,14 @@ mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodi
|
|||
<classname>RestTemplate</classname>. The goal is to define expected
|
||||
requests and provide "stub" responses:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
<programlisting language="java">RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
|
||||
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess("Hello world", "text/plain"));
|
||||
|
||||
// use RestTemplate ...
|
||||
|
||||
mockServer.verify();
|
||||
</programlisting>
|
||||
mockServer.verify();</programlisting>
|
||||
|
||||
<para>In the above example,
|
||||
<classname>MockRestServiceServer</classname> -- the central class for
|
||||
|
|
@ -2893,8 +3333,9 @@ mockServer.verify();
|
|||
|
||||
<para>Once expected requests and stub responses have been defined, the
|
||||
<classname>RestTemplate</classname> can be used in client-side code as
|
||||
usual. At the end of the tests <code>mockServer.verify()</code> can be
|
||||
used to verify that all expected requests were performed.</para>
|
||||
usual. At the end of the tests <literal>mockServer.verify()</literal>
|
||||
can be used to verify that all expected requests were
|
||||
performed.</para>
|
||||
|
||||
<section xml:id="spring-mvc-test-client-static-imports">
|
||||
<title>Static Imports</title>
|
||||
|
|
|
|||
Loading…
Reference in New Issue