2015-03-03 18:38:01 +08:00
|
|
|
|
[[testing]]
|
|
|
|
|
= Testing
|
2017-01-05 00:51:58 +08:00
|
|
|
|
:doc-root: https://docs.spring.io
|
|
|
|
|
:api-spring-framework: {doc-root}/spring-framework/docs/{spring-version}/javadoc-api/org/springframework
|
|
|
|
|
:doc-spring-boot: {doc-root}/spring-boot/docs/current/reference
|
2017-03-29 20:20:12 +08:00
|
|
|
|
:toc: left
|
|
|
|
|
:toclevels: 2
|
2017-09-18 18:16:47 +08:00
|
|
|
|
:docinfo1:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
The adoption of the test-driven-development (TDD) approach to software
|
|
|
|
|
development is certainly advocated by the Spring team, and so coverage of Spring's
|
|
|
|
|
support for integration testing is covered (alongside best practices for unit testing).
|
|
|
|
|
The Spring team has found that the correct use of IoC certainly does make both unit and
|
|
|
|
|
integration testing easier (in that the presence of setter methods and appropriate
|
|
|
|
|
constructors on classes makes them easier to wire together in a test without having to
|
|
|
|
|
set up service locator registries and suchlike)... the chapter dedicated solely to
|
|
|
|
|
testing will hopefully convince you of this as well.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testing-introduction]]
|
|
|
|
|
== Introduction to Spring Testing
|
|
|
|
|
Testing is an integral part of enterprise software development. This chapter focuses on
|
|
|
|
|
the value-add of the IoC principle to <<unit-testing,unit testing>> and on the benefits
|
|
|
|
|
of the Spring Framework's support for <<integration-testing,integration testing>>. __(A
|
|
|
|
|
thorough treatment of testing in the enterprise is beyond the scope of this reference
|
|
|
|
|
manual.)__
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[unit-testing]]
|
|
|
|
|
== Unit Testing
|
|
|
|
|
Dependency Injection should make your code less dependent on the container than it would
|
|
|
|
|
be with traditional Java EE development. The POJOs that make up your application should
|
|
|
|
|
be testable in JUnit or TestNG tests, with objects simply instantiated using the `new`
|
|
|
|
|
operator, __without Spring or any other container__. You can use <<mock-objects,mock
|
|
|
|
|
objects>> (in conjunction with other valuable testing techniques) to test your code in
|
|
|
|
|
isolation. If you follow the architecture recommendations for Spring, the resulting
|
|
|
|
|
clean layering and componentization of your codebase will facilitate easier unit
|
|
|
|
|
testing. For example, you can test service layer objects by stubbing or mocking DAO or
|
|
|
|
|
Repository interfaces, without needing to access persistent data while running unit
|
|
|
|
|
tests.
|
|
|
|
|
|
|
|
|
|
True unit tests typically run extremely quickly, as there is no runtime infrastructure
|
|
|
|
|
to set up. Emphasizing true unit tests as part of your development methodology will
|
|
|
|
|
boost your productivity. You may not need this section of the testing chapter to help
|
|
|
|
|
you write effective unit tests for your IoC-based applications. For certain unit testing
|
|
|
|
|
scenarios, however, the Spring Framework provides the following mock objects and testing
|
|
|
|
|
support classes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[mock-objects]]
|
|
|
|
|
=== Mock Objects
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[mock-objects-env]]
|
|
|
|
|
==== Environment
|
|
|
|
|
The `org.springframework.mock.env` package contains mock implementations of the
|
2017-03-29 20:20:12 +08:00
|
|
|
|
`Environment` and `PropertySource` abstractions (see
|
|
|
|
|
<<core.adoc#beans-definition-profiles, Bean definition profiles>>
|
|
|
|
|
and <<core.adoc#beans-property-source-abstraction, PropertySource abstraction>>).
|
|
|
|
|
`MockEnvironment` and `MockPropertySource` are useful for developing
|
|
|
|
|
__out-of-container__ tests for code that depends on environment-specific properties.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[mock-objects-jndi]]
|
|
|
|
|
==== JNDI
|
|
|
|
|
The `org.springframework.mock.jndi` package contains an implementation of the JNDI SPI,
|
|
|
|
|
which you can use to set up a simple JNDI environment for test suites or stand-alone
|
2016-06-11 18:51:38 +08:00
|
|
|
|
applications. If, for example, JDBC ``DataSource``s get bound to the same JNDI names in
|
2015-03-03 18:38:01 +08:00
|
|
|
|
test code as within a Java EE container, you can reuse both application code and
|
|
|
|
|
configuration in testing scenarios without modification.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[mock-objects-servlet]]
|
|
|
|
|
==== Servlet API
|
|
|
|
|
The `org.springframework.mock.web` package contains a comprehensive set of Servlet API
|
2015-07-22 00:26:03 +08:00
|
|
|
|
mock objects, which are useful for testing web contexts, controllers, and filters. These
|
|
|
|
|
mock objects are targeted at usage with Spring's Web MVC framework and are generally more
|
|
|
|
|
convenient to use than dynamic mock objects such as http://www.easymock.org[EasyMock] or
|
2017-09-30 08:21:01 +08:00
|
|
|
|
alternative Servlet API mock objects such as http://www.mockobjects.com[MockObjects].
|
2015-07-22 00:26:03 +08:00
|
|
|
|
|
2017-09-30 08:21:01 +08:00
|
|
|
|
[TIP]
|
|
|
|
|
====
|
|
|
|
|
Since Spring Framework 5.0, the mock objects in `org.springframework.mock.web` are based
|
|
|
|
|
on the Servlet 4.0 API.
|
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-09-30 08:21:01 +08:00
|
|
|
|
The Spring MVC Test framework builds on the mock Servlet API objects to provide an
|
|
|
|
|
integration test framework for Spring MVC. See
|
|
|
|
|
<<spring-mvc-test-framework,_Spring MVC Test_>>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[mock-objects-web-reactive]]
|
|
|
|
|
==== Spring Web Reactive
|
|
|
|
|
The `org.springframework.mock.http.server.reactive` package contains mock request and
|
|
|
|
|
response objects for testing Spring WebFlux applications. There is also a
|
|
|
|
|
`MockWebServerExchange` in `org.springframework.mock.web.server` package that uses
|
|
|
|
|
those mock request and response.
|
|
|
|
|
|
|
|
|
|
`WebTestClient` builds on these mock objects and to provide integration testing
|
|
|
|
|
of WebFlux server endpoints without running an HTTP server.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[unit-testing-support-classes]]
|
|
|
|
|
=== Unit Testing support Classes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[unit-testing-utilities]]
|
2015-07-21 23:32:30 +08:00
|
|
|
|
==== General testing utilities
|
|
|
|
|
The `org.springframework.test.util` package contains several general purpose utilities
|
|
|
|
|
for use in unit and integration testing.
|
|
|
|
|
|
|
|
|
|
`ReflectionTestUtils` is a collection of reflection-based utility methods. Developers use
|
|
|
|
|
these methods in testing scenarios where they need to change the value of a constant, set
|
2015-07-22 00:24:52 +08:00
|
|
|
|
a non-`public` field, invoke a non-`public` setter method, or invoke a non-`public`
|
2015-07-21 23:10:00 +08:00
|
|
|
|
_configuration_ or _lifecycle_ callback method when testing application code involving
|
|
|
|
|
use cases such as the following.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
* ORM frameworks such as JPA and Hibernate that condone `private` or `protected` field
|
|
|
|
|
access as opposed to `public` setter methods for properties in a domain entity.
|
2015-07-21 23:10:00 +08:00
|
|
|
|
* Spring's support for annotations such as `@Autowired`, `@Inject`, and `@Resource`,
|
2015-03-03 18:38:01 +08:00
|
|
|
|
which provides dependency injection for `private` or `protected` fields, setter
|
|
|
|
|
methods, and configuration methods.
|
2015-07-21 23:10:00 +08:00
|
|
|
|
* Use of annotations such as `@PostConstruct` and `@PreDestroy` for lifecycle callback
|
|
|
|
|
methods.
|
|
|
|
|
|
2015-07-21 23:32:30 +08:00
|
|
|
|
`AopTestUtils` is a collection of AOP-related utility methods. These methods can be used
|
|
|
|
|
to obtain a reference to the underlying target object hidden behind one or more Spring
|
|
|
|
|
proxies. For example, if you have configured a bean as a dynamic mock using a library
|
|
|
|
|
like EasyMock or Mockito and the mock is wrapped in a Spring proxy, you may need direct
|
|
|
|
|
access to the underlying mock in order to configure expectations on it and perform
|
|
|
|
|
verifications. For Spring's core AOP utilities, see `AopUtils` and `AopProxyUtils`.
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[unit-testing-spring-mvc]]
|
|
|
|
|
==== Spring MVC
|
|
|
|
|
The `org.springframework.test.web` package contains `ModelAndViewAssert`, which you can
|
|
|
|
|
use in combination with JUnit, TestNG, or any other testing framework for unit tests
|
|
|
|
|
dealing with Spring MVC `ModelAndView` objects.
|
|
|
|
|
|
|
|
|
|
.Unit testing Spring MVC Controllers
|
|
|
|
|
[TIP]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2016-06-11 18:51:38 +08:00
|
|
|
|
To unit test your Spring MVC ``Controller``s as POJOs, use `ModelAndViewAssert` combined
|
2015-07-22 00:26:03 +08:00
|
|
|
|
with `MockHttpServletRequest`, `MockHttpSession`, and so on from Spring's
|
|
|
|
|
<<mock-objects-servlet, Servlet API mocks>>. For thorough integration testing of your
|
2016-06-11 18:51:38 +08:00
|
|
|
|
Spring MVC and REST ``Controller``s in conjunction with your `WebApplicationContext`
|
2015-07-22 00:26:03 +08:00
|
|
|
|
configuration for Spring MVC, use the <<spring-mvc-test-framework,_Spring MVC Test
|
|
|
|
|
Framework_>> instead.
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing]]
|
|
|
|
|
== Integration Testing
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-overview]]
|
|
|
|
|
=== Overview
|
|
|
|
|
It is important to be able to perform some integration testing without requiring
|
|
|
|
|
deployment to your application server or connecting to other enterprise infrastructure.
|
|
|
|
|
This will enable you to test things such as:
|
|
|
|
|
|
|
|
|
|
* The correct wiring of your Spring IoC container contexts.
|
|
|
|
|
* Data access using JDBC or an ORM tool. This would include such things as the
|
|
|
|
|
correctness of SQL statements, Hibernate queries, JPA entity mappings, etc.
|
|
|
|
|
|
|
|
|
|
The Spring Framework provides first-class support for integration testing in the
|
|
|
|
|
`spring-test` module. The name of the actual JAR file might include the release version
|
|
|
|
|
and might also be in the long `org.springframework.test` form, depending on where you
|
2017-03-29 20:20:12 +08:00
|
|
|
|
get it from (see the <<core.adoc#dependency-management,section on Dependency Management>> for an
|
2015-03-03 18:38:01 +08:00
|
|
|
|
explanation). This library includes the `org.springframework.test` package, which
|
|
|
|
|
contains valuable classes for integration testing with a Spring container. This testing
|
|
|
|
|
does not rely on an application server or other deployment environment. Such tests are
|
|
|
|
|
slower to run than unit tests but much faster than the equivalent Selenium tests or remote
|
|
|
|
|
tests that rely on deployment to an application server.
|
|
|
|
|
|
|
|
|
|
In Spring 2.5 and later, unit and integration testing support is provided in the form of
|
|
|
|
|
the annotation-driven <<testcontext-framework,Spring TestContext Framework>>. The
|
|
|
|
|
TestContext framework is agnostic of the actual testing framework in use, thus allowing
|
|
|
|
|
instrumentation of tests in various environments including JUnit, TestNG, and so on.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-goals]]
|
|
|
|
|
=== Goals of Integration Testing
|
|
|
|
|
Spring's integration testing support has the following primary goals:
|
|
|
|
|
|
|
|
|
|
* To manage <<testing-ctx-management,Spring IoC container caching>> between test
|
|
|
|
|
execution.
|
|
|
|
|
* To provide <<testing-fixture-di,Dependency Injection of test fixture instances>>.
|
|
|
|
|
* To provide <<testing-tx,transaction management>> appropriate to integration testing.
|
|
|
|
|
* To supply <<testing-support-classes,Spring-specific base classes>> that assist
|
|
|
|
|
developers in writing integration tests.
|
|
|
|
|
|
|
|
|
|
The next few sections describe each goal and provide links to implementation and
|
|
|
|
|
configuration details.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testing-ctx-management]]
|
|
|
|
|
==== Context management and caching
|
|
|
|
|
The Spring TestContext Framework provides consistent loading of Spring
|
2016-06-11 18:51:38 +08:00
|
|
|
|
``ApplicationContext``s and ``WebApplicationContext``s as well as caching of those
|
2015-03-03 18:38:01 +08:00
|
|
|
|
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.
|
|
|
|
|
|
2016-04-06 20:36:36 +08:00
|
|
|
|
Test classes typically declare either an array of __resource locations__ for XML or Groovy
|
2015-03-03 18:38:01 +08:00
|
|
|
|
configuration metadata -- often in the classpath -- or an array of __annotated classes__
|
|
|
|
|
that is used to configure the application. These locations or classes are the same as or
|
2016-04-06 20:36:36 +08:00
|
|
|
|
similar to those specified in `web.xml` or other configuration files for production
|
|
|
|
|
deployments.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
By default, once loaded, the configured `ApplicationContext` is reused for each test.
|
|
|
|
|
Thus the setup cost is incurred only once per test suite, and subsequent test execution
|
|
|
|
|
is much faster. In this context, the term __test suite__ means all tests run in the same
|
|
|
|
|
JVM -- for example, all tests run from an Ant, Maven, or Gradle build for a given
|
|
|
|
|
project or module. In the unlikely case that a test corrupts the application context and
|
|
|
|
|
requires reloading -- for example, by modifying a bean definition or the state of an
|
|
|
|
|
application object -- the TestContext framework can be configured to reload the
|
|
|
|
|
configuration and rebuild the application context before executing the next test.
|
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management>> and <<testcontext-ctx-management-caching>> with the
|
|
|
|
|
TestContext framework.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testing-fixture-di]]
|
|
|
|
|
==== Dependency Injection of test fixtures
|
|
|
|
|
When the TestContext framework loads your application context, it can optionally
|
|
|
|
|
configure instances of your test classes via Dependency Injection. This provides a
|
|
|
|
|
convenient mechanism for setting up test fixtures using preconfigured beans from your
|
|
|
|
|
application context. A strong benefit here is that you can reuse application contexts
|
|
|
|
|
across various testing scenarios (e.g., for configuring Spring-managed object graphs,
|
2016-06-11 18:51:38 +08:00
|
|
|
|
transactional proxies, ``DataSource``s, etc.), thus avoiding the need to duplicate
|
2015-03-03 18:38:01 +08:00
|
|
|
|
complex test fixture setup for individual test cases.
|
|
|
|
|
|
|
|
|
|
As an example, consider the scenario where we have a class, `HibernateTitleRepository`,
|
|
|
|
|
that implements data access logic for a `Title` domain entity. We want to write
|
|
|
|
|
integration tests that test the following areas:
|
|
|
|
|
|
|
|
|
|
* The Spring configuration: basically, is everything related to the configuration of the
|
|
|
|
|
`HibernateTitleRepository` bean correct and present?
|
|
|
|
|
* The Hibernate mapping file configuration: is everything mapped correctly, and are the
|
|
|
|
|
correct lazy-loading settings in place?
|
|
|
|
|
* The logic of the `HibernateTitleRepository`: does the configured instance of this
|
|
|
|
|
class perform as anticipated?
|
|
|
|
|
|
|
|
|
|
See dependency injection of test fixtures with the <<testcontext-fixture-di,TestContext
|
|
|
|
|
framework>>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testing-tx]]
|
|
|
|
|
==== Transaction management
|
|
|
|
|
One common issue in tests that access a real database is their effect on the state of
|
|
|
|
|
the persistence store. Even when you're using a development database, changes to the
|
|
|
|
|
state may affect future tests. Also, many operations -- such as inserting or modifying
|
|
|
|
|
persistent data -- cannot be performed (or verified) outside a transaction.
|
|
|
|
|
|
|
|
|
|
The TestContext framework addresses this issue. By default, the framework will create
|
|
|
|
|
and roll back a transaction for each test. You simply write code that can assume the
|
|
|
|
|
existence of a transaction. If you call transactionally proxied objects in your tests,
|
|
|
|
|
they will behave correctly, according to their configured transactional semantics. In
|
|
|
|
|
addition, if a test method deletes the contents of selected tables while running within
|
|
|
|
|
the transaction managed for the test, the transaction will roll back by default, and the
|
|
|
|
|
database will return to its state prior to execution of the test. Transactional support
|
|
|
|
|
is provided to a test via a `PlatformTransactionManager` bean defined in the test's
|
|
|
|
|
application context.
|
|
|
|
|
|
|
|
|
|
If you want a transaction to commit -- unusual, but occasionally useful when you want a
|
|
|
|
|
particular test to populate or modify the database -- the TestContext framework can be
|
|
|
|
|
instructed to cause the transaction to commit instead of roll back via the
|
2015-09-11 01:35:17 +08:00
|
|
|
|
<<integration-testing-annotations, `@Commit`>> annotation.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
See transaction management with the <<testcontext-tx,TestContext framework>>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testing-support-classes]]
|
|
|
|
|
==== Support classes for integration testing
|
|
|
|
|
The Spring TestContext Framework provides several `abstract` support classes that
|
|
|
|
|
simplify the writing of integration tests. These base test classes provide well-defined
|
|
|
|
|
hooks into the testing framework as well as convenient instance variables and methods,
|
|
|
|
|
which enable you to access:
|
|
|
|
|
|
|
|
|
|
* The `ApplicationContext`, for performing explicit bean lookups or testing the state of
|
|
|
|
|
the context as a whole.
|
|
|
|
|
* A `JdbcTemplate`, for executing SQL statements to query the database. Such queries can
|
|
|
|
|
be used to confirm database state both __prior to__ and __after__ execution of
|
|
|
|
|
database-related application code, and Spring ensures that such queries run in the
|
|
|
|
|
scope of the same transaction as the application code. When used in conjunction with
|
|
|
|
|
an ORM tool, be sure to avoid <<testcontext-tx-false-positives,false positives>>.
|
|
|
|
|
|
|
|
|
|
In addition, you may want to create your own custom, application-wide superclass with
|
|
|
|
|
instance variables and methods specific to your project.
|
|
|
|
|
|
|
|
|
|
See support classes for the <<testcontext-support-classes,TestContext framework>>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-support-jdbc]]
|
|
|
|
|
=== JDBC Testing Support
|
|
|
|
|
The `org.springframework.test.jdbc` package contains `JdbcTestUtils`, which is a
|
|
|
|
|
collection of JDBC related utility functions intended to simplify standard database
|
|
|
|
|
testing scenarios. Specifically, `JdbcTestUtils` provides the following static utility
|
|
|
|
|
methods.
|
|
|
|
|
|
|
|
|
|
* `countRowsInTable(..)`: counts the number of rows in the given table
|
|
|
|
|
* `countRowsInTableWhere(..)`: counts the number of rows in the given table, using
|
|
|
|
|
the provided `WHERE` clause
|
|
|
|
|
* `deleteFromTables(..)`: deletes all rows from the specified tables
|
|
|
|
|
* `deleteFromTableWhere(..)`: deletes rows from the given table, using the provided
|
|
|
|
|
`WHERE` clause
|
|
|
|
|
* `dropTables(..)`: drops the specified tables
|
|
|
|
|
|
|
|
|
|
__Note that <<testcontext-support-classes-junit4,
|
|
|
|
|
`AbstractTransactionalJUnit4SpringContextTests`>> and
|
|
|
|
|
<<testcontext-support-classes-testng, `AbstractTransactionalTestNGSpringContextTests`>>
|
|
|
|
|
provide convenience methods which delegate to the aforementioned methods in
|
|
|
|
|
`JdbcTestUtils`.__
|
|
|
|
|
|
|
|
|
|
The `spring-jdbc` module provides support for configuring and launching an embedded
|
|
|
|
|
database which can be used in integration tests that interact with a database. For
|
2017-03-29 20:20:12 +08:00
|
|
|
|
details, see <<data-access.adoc#jdbc-embedded-database-support, Embedded database support>>
|
|
|
|
|
and <<data-access.adoc#jdbc-embedded-database-dao-testing, Testing data access logic
|
|
|
|
|
with an embedded database>>.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations]]
|
|
|
|
|
=== Annotations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-spring]]
|
|
|
|
|
==== Spring Testing Annotations
|
|
|
|
|
The Spring Framework provides the following set of __Spring-specific__ annotations that
|
|
|
|
|
you can use in your unit and integration tests in conjunction with the TestContext
|
|
|
|
|
framework. Refer to the corresponding javadocs for further information, including
|
|
|
|
|
default attribute values, attribute aliases, and so on.
|
|
|
|
|
|
2016-06-10 00:55:36 +08:00
|
|
|
|
===== @BootstrapWith
|
|
|
|
|
`@BootstrapWith` is a class-level annotation that is used to configure how the _Spring
|
|
|
|
|
TestContext Framework_ is bootstrapped. Specifically, `@BootstrapWith` is used to specify
|
|
|
|
|
a custom `TestContextBootstrapper`. Consult the <<testcontext-bootstrapping,Bootstrapping
|
|
|
|
|
the TestContext framework>> section for further details.
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @ContextConfiguration
|
|
|
|
|
`@ContextConfiguration` defines class-level metadata that is used to determine how to
|
|
|
|
|
load and configure an `ApplicationContext` for integration tests. Specifically,
|
|
|
|
|
`@ContextConfiguration` declares the application context resource `locations` or the
|
|
|
|
|
annotated `classes` that will be used to load the context.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2016-04-06 20:36:36 +08:00
|
|
|
|
Resource locations are typically XML configuration files or Groovy scripts located in
|
|
|
|
|
the classpath; whereas, annotated classes are typically `@Configuration` classes. However,
|
|
|
|
|
resource locations can also refer to files and scripts in the file system, and annotated
|
|
|
|
|
classes can be component classes, etc.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@ContextConfiguration**("/test-config.xml")
|
|
|
|
|
public class XmlApplicationContextTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@ContextConfiguration**(**classes** = TestConfig.class)
|
|
|
|
|
public class ConfigClassApplicationContextTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
As an alternative or in addition to declaring resource locations or annotated classes,
|
|
|
|
|
`@ContextConfiguration` may be used to declare `ApplicationContextInitializer` classes.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@ContextConfiguration**(**initializers** = CustomContextIntializer.class)
|
|
|
|
|
public class ContextInitializerTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
`@ContextConfiguration` may optionally be used to declare the `ContextLoader` strategy
|
|
|
|
|
as well. Note, however, that you typically do not need to explicitly configure the
|
|
|
|
|
loader since the default loader supports either resource `locations` or annotated
|
|
|
|
|
`classes` as well as `initializers`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@ContextConfiguration**(**locations** = "/test-context.xml", **loader** = CustomContextLoader.class)
|
|
|
|
|
public class CustomLoaderXmlApplicationContextTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`@ContextConfiguration` provides support for __inheriting__ resource locations or
|
|
|
|
|
configuration classes as well as context initializers declared by superclasses by
|
|
|
|
|
default.
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management>> and the `@ContextConfiguration` javadocs for
|
|
|
|
|
further details.
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @WebAppConfiguration
|
|
|
|
|
`@WebAppConfiguration` is a class-level annotation that is used to declare that the
|
|
|
|
|
`ApplicationContext` loaded for an integration test should be a `WebApplicationContext`.
|
|
|
|
|
The mere presence of `@WebAppConfiguration` on a test class ensures that a
|
|
|
|
|
`WebApplicationContext` will be loaded for the test, using the default value of
|
|
|
|
|
`"file:src/main/webapp"` for the path to the root of the web application (i.e., the
|
|
|
|
|
__resource base path__). The resource base path is used behind the scenes to create a
|
|
|
|
|
`MockServletContext` which serves as the `ServletContext` for the test's
|
|
|
|
|
`WebApplicationContext`.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
**@WebAppConfiguration**
|
|
|
|
|
public class WebAppTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
To override the default, specify a different base resource path via the __implicit__
|
|
|
|
|
`value` attribute. Both `classpath:` and `file:` resource prefixes are supported. If no
|
|
|
|
|
resource prefix is supplied the path is assumed to be a file system resource.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
**@WebAppConfiguration("classpath:test-web-resources")**
|
|
|
|
|
public class WebAppTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Note that `@WebAppConfiguration` must be used in conjunction with
|
|
|
|
|
`@ContextConfiguration`, either within a single test class or within a test class
|
|
|
|
|
hierarchy. See the `@WebAppConfiguration` javadocs for further details.
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @ContextHierarchy
|
|
|
|
|
`@ContextHierarchy` is a class-level annotation that is used to define a hierarchy of
|
2016-06-11 18:51:38 +08:00
|
|
|
|
``ApplicationContext``s for integration tests. `@ContextHierarchy` should be declared
|
2016-06-09 23:03:44 +08:00
|
|
|
|
with a list of one or more `@ContextConfiguration` instances, each of which defines a
|
|
|
|
|
level in the context hierarchy. The following examples demonstrate the use of
|
|
|
|
|
`@ContextHierarchy` within a single test class; however, `@ContextHierarchy` can also be
|
|
|
|
|
used within a test class hierarchy.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextHierarchy({
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration("/parent-config.xml"),
|
|
|
|
|
@ContextConfiguration("/child-config.xml")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
})
|
|
|
|
|
public class ContextHierarchyTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@WebAppConfiguration
|
|
|
|
|
@ContextHierarchy({
|
|
|
|
|
@ContextConfiguration(classes = AppConfig.class),
|
|
|
|
|
@ContextConfiguration(classes = WebConfig.class)
|
|
|
|
|
})
|
|
|
|
|
public class WebIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
If you need to merge or override the configuration for a given level of the context
|
|
|
|
|
hierarchy within a test class hierarchy, you must explicitly name that level by
|
|
|
|
|
supplying the same value to the `name` attribute in `@ContextConfiguration` at each
|
|
|
|
|
corresponding level in the class hierarchy. See
|
|
|
|
|
<<testcontext-ctx-management-ctx-hierarchies>> and the `@ContextHierarchy` javadocs
|
|
|
|
|
for further examples.
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @ActiveProfiles
|
|
|
|
|
`@ActiveProfiles` is a class-level annotation that is used to declare which __bean
|
|
|
|
|
definition profiles__ should be active when loading an `ApplicationContext` for an
|
|
|
|
|
integration test.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
**@ActiveProfiles**("dev")
|
|
|
|
|
public class DeveloperTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
**@ActiveProfiles**({"dev", "integration"})
|
|
|
|
|
public class DeveloperIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`@ActiveProfiles` provides support for __inheriting__ active bean definition profiles
|
|
|
|
|
declared by superclasses by default. It is also possible to resolve active bean
|
|
|
|
|
definition profiles programmatically by implementing a custom
|
|
|
|
|
<<testcontext-ctx-management-env-profiles-ActiveProfilesResolver,`ActiveProfilesResolver`>>
|
|
|
|
|
and registering it via the `resolver` attribute of `@ActiveProfiles`.
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management-env-profiles>> and the `@ActiveProfiles` javadocs
|
|
|
|
|
for examples and further details.
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @TestPropertySource
|
|
|
|
|
`@TestPropertySource` is a class-level annotation that is used to configure the locations
|
|
|
|
|
of properties files and inlined properties to be added to the set of `PropertySources` in
|
|
|
|
|
the `Environment` for an `ApplicationContext` loaded for an integration test.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
Test property sources have higher precedence than those loaded from the operating
|
|
|
|
|
system's environment or Java system properties as well as property sources added by the
|
|
|
|
|
application declaratively via `@PropertySource` or programmatically. Thus, test property
|
|
|
|
|
sources can be used to selectively override properties defined in system and application
|
|
|
|
|
property sources. Furthermore, inlined properties have higher precedence than properties
|
|
|
|
|
loaded from resource locations.
|
|
|
|
|
|
|
|
|
|
The following example demonstrates how to declare a properties file from the classpath.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
**@TestPropertySource**("/test.properties")
|
|
|
|
|
public class MyIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
The following example demonstrates how to declare _inlined_ properties.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
**@TestPropertySource**(properties = { "timezone = GMT", "port: 4242" })
|
|
|
|
|
public class MyIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @DirtiesContext
|
|
|
|
|
`@DirtiesContext` indicates that the underlying Spring `ApplicationContext` has been
|
|
|
|
|
__dirtied__ during the execution of a test (i.e., modified or corrupted in some manner --
|
|
|
|
|
for example, by changing the state of a singleton bean) and should be closed. When an
|
|
|
|
|
application context is marked __dirty__, it is removed from the testing framework's cache
|
|
|
|
|
and closed. As a consequence, the underlying Spring container will be rebuilt for any
|
2015-03-23 01:42:41 +08:00
|
|
|
|
subsequent test that requires a context with the same configuration metadata.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
`@DirtiesContext` can be used as both a class-level and method-level annotation within
|
2015-03-23 01:42:41 +08:00
|
|
|
|
the same class or class hierarchy. In such scenarios, the `ApplicationContext` is marked
|
|
|
|
|
as __dirty__ before or after any such annotated method as well as before or after the
|
|
|
|
|
current test class, depending on the configured `methodMode` and `classMode`.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
The following examples explain when the context would be dirtied for various
|
|
|
|
|
configuration scenarios:
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* Before the current test class, when declared on a class with class mode set to
|
2015-03-23 01:42:41 +08:00
|
|
|
|
`BEFORE_CLASS`.
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@DirtiesContext(classMode = BEFORE_CLASS)**
|
|
|
|
|
public class FreshContextTests {
|
|
|
|
|
// some tests that require a new Spring container
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* After the current test class, when declared on a class with class mode set to
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`AFTER_CLASS` (i.e., the default class mode).
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@DirtiesContext**
|
|
|
|
|
public class ContextDirtyingTests {
|
|
|
|
|
// some tests that result in the Spring container being dirtied
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* Before each test method in the current test class, when declared on a class with class
|
2015-03-23 01:42:41 +08:00
|
|
|
|
mode set to `BEFORE_EACH_TEST_METHOD.`
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)**
|
|
|
|
|
public class FreshContextTests {
|
|
|
|
|
// some tests that require a new Spring container
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* After each test method in the current test class, when declared on a class with class
|
2015-03-03 18:38:01 +08:00
|
|
|
|
mode set to `AFTER_EACH_TEST_METHOD.`
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2015-03-23 01:42:41 +08:00
|
|
|
|
**@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)**
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class ContextDirtyingTests {
|
|
|
|
|
// some tests that result in the Spring container being dirtied
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* Before the current test, when declared on a method with the method mode set to
|
2015-03-23 01:42:41 +08:00
|
|
|
|
`BEFORE_METHOD`.
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@DirtiesContext(methodMode = BEFORE_METHOD)**
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessWhichRequiresFreshAppCtx() {
|
|
|
|
|
// some logic that requires a new Spring container
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* After the current test, when declared on a method with the method mode set to
|
2015-03-23 01:42:41 +08:00
|
|
|
|
`AFTER_METHOD` (i.e., the default method mode).
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@DirtiesContext**
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessWhichDirtiesAppCtx() {
|
|
|
|
|
// some logic that results in the Spring container being dirtied
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
If `@DirtiesContext` is used in a test whose context is configured as part of a context
|
|
|
|
|
hierarchy via `@ContextHierarchy`, the `hierarchyMode` flag can be used to control how
|
|
|
|
|
the context cache is cleared. By default an __exhaustive__ algorithm will be used that
|
|
|
|
|
clears the context cache including not only the current level but also all other context
|
|
|
|
|
hierarchies that share an ancestor context common to the current test; all
|
2016-06-11 18:51:38 +08:00
|
|
|
|
``ApplicationContext``s that reside in a sub-hierarchy of the common ancestor context
|
2015-03-03 18:38:01 +08:00
|
|
|
|
will be removed from the context cache and closed. If the __exhaustive__ algorithm is
|
|
|
|
|
overkill for a particular use case, the simpler __current level__ algorithm can be
|
|
|
|
|
specified instead, as seen below.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextHierarchy({
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration("/parent-config.xml"),
|
|
|
|
|
@ContextConfiguration("/child-config.xml")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
})
|
|
|
|
|
public class BaseTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ExtendedTests extends BaseTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
2015-03-23 01:42:41 +08:00
|
|
|
|
@DirtiesContext(**hierarchyMode = CURRENT_LEVEL**)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public void test() {
|
|
|
|
|
// some logic that results in the child context being dirtied
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
For further details regarding the `EXHAUSTIVE` and `CURRENT_LEVEL` algorithms see the
|
|
|
|
|
`DirtiesContext.HierarchyMode` javadocs.
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @TestExecutionListeners
|
|
|
|
|
`@TestExecutionListeners` defines class-level metadata for configuring the
|
|
|
|
|
`TestExecutionListener` implementations that should be registered with the
|
|
|
|
|
`TestContextManager`. Typically, `@TestExecutionListeners` is used in conjunction with
|
|
|
|
|
`@ContextConfiguration`.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
**@TestExecutionListeners**({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
|
|
|
|
|
public class CustomTestExecutionListenerTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
`@TestExecutionListeners` supports _inherited_ listeners by default. See the javadocs
|
2015-03-03 18:38:01 +08:00
|
|
|
|
for an example and further details.
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @Commit
|
|
|
|
|
`@Commit` indicates that the transaction for a transactional test method should be
|
|
|
|
|
__committed__ after the test method has completed. `@Commit` can be used as a direct
|
|
|
|
|
replacement for `@Rollback(false)` in order to more explicitly convey the intent of the
|
|
|
|
|
code. Analogous to `@Rollback`, `@Commit` may also be declared as a class-level or
|
|
|
|
|
method-level annotation.
|
2015-07-26 03:09:32 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@Commit**
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessWithoutRollback() {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @Rollback
|
|
|
|
|
`@Rollback` indicates whether the transaction for a transactional test method should be
|
|
|
|
|
__rolled back__ after the test method has completed. If `true`, the transaction is rolled
|
|
|
|
|
back; otherwise, the transaction is committed (see also `@Commit`). Rollback semantics
|
|
|
|
|
for integration tests in the Spring TestContext Framework default to `true` even if
|
2015-10-16 22:08:42 +08:00
|
|
|
|
`@Rollback` is not explicitly declared.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2015-07-26 00:22:26 +08:00
|
|
|
|
When declared as a class-level annotation, `@Rollback` defines the default rollback
|
|
|
|
|
semantics for all test methods within the test class hierarchy. When declared as a
|
|
|
|
|
method-level annotation, `@Rollback` defines rollback semantics for the specific test
|
2015-10-16 22:08:42 +08:00
|
|
|
|
method, potentially overriding class-level `@Rollback` or `@Commit` semantics.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@Rollback**(false)
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessWithoutRollback() {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @BeforeTransaction
|
|
|
|
|
`@BeforeTransaction` indicates that the annotated `void` method should be executed
|
|
|
|
|
__before__ a transaction is started for test methods configured to run within a
|
|
|
|
|
transaction via Spring's `@Transactional` annotation. As of Spring Framework 4.3,
|
|
|
|
|
`@BeforeTransaction` methods are not required to be `public` and may be declared on Java
|
|
|
|
|
8 based interface default methods.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@BeforeTransaction**
|
2016-02-29 06:36:21 +08:00
|
|
|
|
void beforeTransaction() {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// logic to be executed before a transaction is started
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @AfterTransaction
|
|
|
|
|
`@AfterTransaction` indicates that the annotated `void` method should be executed
|
|
|
|
|
__after__ a transaction is ended for test methods configured to run within a transaction
|
|
|
|
|
via Spring's `@Transactional` annotation. As of Spring Framework 4.3, `@AfterTransaction`
|
|
|
|
|
methods are not required to be `public` and may be declared on Java 8 based interface
|
|
|
|
|
default methods.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@AfterTransaction**
|
2016-02-29 06:36:21 +08:00
|
|
|
|
void afterTransaction() {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// logic to be executed after a transaction has ended
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @Sql
|
|
|
|
|
`@Sql` is used to annotate a test class or test method to configure SQL scripts to be
|
|
|
|
|
executed against a given database during integration tests.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
**@Sql**({"/test-schema.sql", "/test-user-data.sql"})
|
|
|
|
|
public void userTest {
|
|
|
|
|
// execute code that relies on the test schema and test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
See <<testcontext-executing-sql-declaratively>> for further details.
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @SqlConfig
|
|
|
|
|
`@SqlConfig` defines metadata that is used to determine how to parse and execute SQL
|
|
|
|
|
scripts configured via the `@Sql` annotation.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@Sql(
|
|
|
|
|
scripts = "/test-user-data.sql",
|
|
|
|
|
config = **@SqlConfig**(commentPrefix = "`", separator = "@@")
|
|
|
|
|
)
|
|
|
|
|
public void userTest {
|
|
|
|
|
// execute code that relies on the test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @SqlGroup
|
|
|
|
|
`@SqlGroup` is a container annotation that aggregates several `@Sql` annotations.
|
|
|
|
|
`@SqlGroup` can be used natively, declaring several nested `@Sql` annotations, or it can
|
|
|
|
|
be used in conjunction with Java 8's support for repeatable annotations, where `@Sql` can
|
|
|
|
|
simply be declared several times on the same class or method, implicitly generating this
|
|
|
|
|
container annotation.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
**@SqlGroup**({
|
|
|
|
|
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@Sql("/test-user-data.sql")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
)}
|
|
|
|
|
public void userTest {
|
|
|
|
|
// execute code that uses the test schema and test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-standard]]
|
|
|
|
|
==== Standard Annotation Support
|
|
|
|
|
The following annotations are supported with standard semantics for all configurations
|
|
|
|
|
of the Spring TestContext Framework. Note that these annotations are not specific to
|
|
|
|
|
tests and can be used anywhere in the Spring Framework.
|
|
|
|
|
|
|
|
|
|
* `@Autowired`
|
|
|
|
|
* `@Qualifier`
|
|
|
|
|
* `@Resource` (javax.annotation) _if JSR-250 is present_
|
2016-08-19 19:52:39 +08:00
|
|
|
|
* `@ManagedBean` (javax.annotation) _if JSR-250 is present_
|
2015-03-03 18:38:01 +08:00
|
|
|
|
* `@Inject` (javax.inject) _if JSR-330 is present_
|
|
|
|
|
* `@Named` (javax.inject) _if JSR-330 is present_
|
|
|
|
|
* `@PersistenceContext` (javax.persistence) _if JPA is present_
|
|
|
|
|
* `@PersistenceUnit` (javax.persistence) _if JPA is present_
|
|
|
|
|
* `@Required`
|
|
|
|
|
* `@Transactional`
|
|
|
|
|
|
|
|
|
|
.JSR-250 Lifecycle Annotations
|
|
|
|
|
[NOTE]
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
In the Spring TestContext Framework `@PostConstruct` and `@PreDestroy` may be used with
|
|
|
|
|
standard semantics on any application components configured in the `ApplicationContext`;
|
|
|
|
|
however, these lifecycle annotations have limited usage within an actual test class.
|
|
|
|
|
|
|
|
|
|
If a method within a test class is annotated with `@PostConstruct`, that method will be
|
|
|
|
|
executed before any __before__ methods of the underlying test framework (e.g., methods
|
2017-09-27 22:25:16 +08:00
|
|
|
|
annotated with JUnit Jupiter's `@BeforeEach`), and that will apply for every test method
|
|
|
|
|
in the test class. On the other hand, if a method within a test class is annotated with
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`@PreDestroy`, that method will __never__ be executed. Within a test class it is
|
|
|
|
|
therefore recommended to use test lifecycle callbacks from the underlying test framework
|
|
|
|
|
instead of `@PostConstruct` and `@PreDestroy`.
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
2017-09-27 22:25:16 +08:00
|
|
|
|
[[integration-testing-annotations-junit4]]
|
2016-05-04 00:45:47 +08:00
|
|
|
|
==== Spring JUnit 4 Testing Annotations
|
2017-09-27 22:25:16 +08:00
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
The following annotations are __only__ supported when used in conjunction with the
|
2016-02-28 06:02:55 +08:00
|
|
|
|
<<testcontext-junit4-runner,SpringRunner>>, <<testcontext-junit4-rules,Spring's JUnit
|
2017-09-27 22:25:16 +08:00
|
|
|
|
4 rules>>, or <<testcontext-support-classes-junit4,Spring's JUnit 4 support classes>>.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @IfProfileValue
|
|
|
|
|
`@IfProfileValue` indicates that the annotated test is enabled for a specific testing
|
|
|
|
|
environment. If the configured `ProfileValueSource` returns a matching `value` for the
|
|
|
|
|
provided `name`, the test is enabled. Otherwise, the test will be disabled and
|
|
|
|
|
effectively _ignored_.
|
2015-10-16 06:48:04 +08:00
|
|
|
|
|
2015-07-10 07:38:54 +08:00
|
|
|
|
`@IfProfileValue` can be applied at the class level, the method level, or both.
|
|
|
|
|
Class-level usage of `@IfProfileValue` takes precedence over method-level usage for any
|
|
|
|
|
methods within that class or its subclasses. Specifically, a test is enabled if it is
|
|
|
|
|
enabled both at the class level _and_ at the method level; the absence of
|
|
|
|
|
`@IfProfileValue` means the test is implicitly enabled. This is analogous to the
|
2016-05-04 00:45:47 +08:00
|
|
|
|
semantics of JUnit 4's `@Ignore` annotation, except that the presence of `@Ignore` always
|
2015-07-10 07:38:54 +08:00
|
|
|
|
disables a test.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@IfProfileValue**(**name**="java.vendor", **value**="Oracle Corporation")
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessWhichRunsOnlyOnOracleJvm() {
|
|
|
|
|
// some logic that should run only on Java VMs from Oracle Corporation
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Alternatively, you can configure `@IfProfileValue` with a list of `values` (with __OR__
|
2016-05-04 00:45:47 +08:00
|
|
|
|
semantics) to achieve TestNG-like support for __test groups__ in a JUnit 4 environment.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
Consider the following example:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@IfProfileValue**(**name**="test-groups", **values**={"unit-tests", "integration-tests"})
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
|
|
|
|
|
// some logic that should run only for unit and integration test groups
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @ProfileValueSourceConfiguration
|
|
|
|
|
`@ProfileValueSourceConfiguration` is a class-level annotation that specifies what type
|
|
|
|
|
of `ProfileValueSource` to use when retrieving __profile values__ configured through the
|
|
|
|
|
`@IfProfileValue` annotation. If `@ProfileValueSourceConfiguration` is not declared for a
|
|
|
|
|
test, `SystemProfileValueSource` is used by default.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@ProfileValueSourceConfiguration**(CustomProfileValueSource.class)
|
|
|
|
|
public class CustomProfileValueSourceTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @Timed
|
|
|
|
|
`@Timed` indicates that the annotated test method must finish execution in a specified
|
|
|
|
|
time period (in milliseconds). If the text execution time exceeds the specified time
|
|
|
|
|
period, the test fails.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
The time period includes execution of the test method itself, any repetitions of the
|
|
|
|
|
test (see `@Repeat`), as well as any __set up__ or __tear down__ of the test fixture.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@Timed**(millis=1000)
|
|
|
|
|
public void testProcessWithOneSecondTimeout() {
|
|
|
|
|
// some logic that should not take longer than 1 second to execute
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-05-04 00:45:47 +08:00
|
|
|
|
Spring's `@Timed` annotation has different semantics than JUnit 4's `@Test(timeout=...)`
|
|
|
|
|
support. Specifically, due to the manner in which JUnit 4 handles test execution timeouts
|
2015-03-03 18:38:01 +08:00
|
|
|
|
(that is, by executing the test method in a separate `Thread`), `@Test(timeout=...)`
|
|
|
|
|
preemptively fails the test if the test takes too long. Spring's `@Timed`, on the other
|
|
|
|
|
hand, does not preemptively fail the test but rather waits for the test to complete
|
|
|
|
|
before failing.
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== @Repeat
|
|
|
|
|
`@Repeat` indicates that the annotated test method must be executed repeatedly. The
|
|
|
|
|
number of times that the test method is to be executed is specified in the annotation.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
The scope of execution to be repeated includes execution of the test method itself as
|
|
|
|
|
well as any __set up__ or __tear down__ of the test fixture.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@Repeat**(10)
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessRepeatedly() {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2017-09-27 22:25:16 +08:00
|
|
|
|
[[integration-testing-annotations-junit-jupiter]]
|
|
|
|
|
==== Spring JUnit Jupiter Testing Annotations
|
|
|
|
|
|
|
|
|
|
The following annotations are __only__ supported when used in conjunction with the
|
2017-09-28 05:01:59 +08:00
|
|
|
|
<<testcontext-junit-jupiter-extension,`SpringExtension`>> and JUnit Jupiter (i.e., the
|
|
|
|
|
programming model in JUnit 5).
|
2017-09-27 22:25:16 +08:00
|
|
|
|
|
|
|
|
|
===== @SpringJUnitConfig
|
|
|
|
|
|
|
|
|
|
`@SpringJUnitConfig` is a _composed annotation_ that combines
|
|
|
|
|
`@ExtendWith(SpringExtension.class)` from JUnit Jupiter with `@ContextConfiguration` from
|
|
|
|
|
the Spring TestContext Framework. It can be used at the class level as a drop-in
|
|
|
|
|
replacement for `@ContextConfiguration`. With regard to configuration options, the only
|
|
|
|
|
difference between `@ContextConfiguration` and `@SpringJUnitConfig` is that annotated
|
|
|
|
|
classes may be declared via the `value` attribute in `@SpringJUnitConfig`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@SpringJUnitConfig**(TestConfig.class)
|
|
|
|
|
class ConfigurationClassJUnitJupiterSpringTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@SpringJUnitConfig**(**locations** = "/test-config.xml")
|
|
|
|
|
class XmlJUnitJupiterSpringTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management>> as well as the javadocs for `@SpringJUnitConfig` and
|
|
|
|
|
`@ContextConfiguration` for further details.
|
|
|
|
|
|
|
|
|
|
===== @SpringJUnitWebConfig
|
|
|
|
|
|
|
|
|
|
`@SpringJUnitWebConfig` is a _composed annotation_ that combines
|
|
|
|
|
`@ExtendWith(SpringExtension.class)` from JUnit Jupiter with `@ContextConfiguration` and
|
|
|
|
|
`@WebAppConfiguration` from the Spring TestContext Framework. It can be used at the class
|
|
|
|
|
level as a drop-in replacement for `@ContextConfiguration` and `@WebAppConfiguration`.
|
|
|
|
|
With regard to configuration options, the only difference between `@ContextConfiguration`
|
|
|
|
|
and `@SpringJUnitWebConfig` is that annotated classes may be declared via the `value`
|
|
|
|
|
attribute in `@SpringJUnitWebConfig`. In addition, the `value` attribute from
|
|
|
|
|
`@WebAppConfiguration` can only be overridden via the `resourcePath` attribute in
|
|
|
|
|
`@SpringJUnitWebConfig`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@SpringJUnitWebConfig**(TestConfig.class)
|
|
|
|
|
class ConfigurationClassJUnitJupiterSpringWebTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@SpringJUnitWebConfig**(**locations** = "/test-config.xml")
|
|
|
|
|
class XmlJUnitJupiterSpringWebTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management>> as well as the javadocs for `@SpringJUnitWebConfig`,
|
|
|
|
|
`@ContextConfiguration`, and `@WebAppConfiguration` for further details.
|
|
|
|
|
|
|
|
|
|
===== @EnabledIf
|
|
|
|
|
|
|
|
|
|
`@EnabledIf` is used to signal that the annotated JUnit Jupiter test class or test method
|
|
|
|
|
is _enabled_ and should be executed if the supplied `expression` evaluates to `true`.
|
|
|
|
|
Specifically, if the expression evaluates to `Boolean.TRUE` or a `String` equal to
|
|
|
|
|
`"true"` (ignoring case), the test will be __enabled__. When applied at the class level,
|
|
|
|
|
all test methods within that class are automatically enabled by default as well.
|
|
|
|
|
|
|
|
|
|
Expressions can be any of the following.
|
|
|
|
|
|
2017-09-28 02:05:42 +08:00
|
|
|
|
* <<core.adoc#expressions,Spring Expression Language>> (SpEL) expression – for example:
|
2017-09-27 22:25:16 +08:00
|
|
|
|
- `@EnabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")`
|
2017-09-28 02:05:42 +08:00
|
|
|
|
* Placeholder for a property available in the Spring
|
|
|
|
|
<<core.adoc#beans-environment,`Environment`>> – for example:
|
2017-09-27 22:25:16 +08:00
|
|
|
|
- `@EnabledIf("${smoke.tests.enabled}")`
|
|
|
|
|
* Text literal – for example:
|
|
|
|
|
- `@EnabledIf("true")`
|
|
|
|
|
|
|
|
|
|
Note, however, that a text literal which is _not_ the result of dynamic resolution of a
|
|
|
|
|
property placeholder is of zero practical value since `@EnabledIf("false")` is equivalent
|
|
|
|
|
to `@Disabled` and `@EnabledIf("true")` is logically meaningless.
|
|
|
|
|
|
|
|
|
|
`@EnabledIf` may be used as a meta-annotation to create custom composed annotations. For
|
|
|
|
|
example, a custom `@EnabledOnMac` annotation can be created as follows.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Target({ ElementType.TYPE, ElementType.METHOD })
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
|
|
|
@EnabledIf(
|
|
|
|
|
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
|
|
|
|
|
reason = "Enabled on Mac OS"
|
|
|
|
|
)
|
|
|
|
|
public @interface EnabledOnMac {}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
===== @DisabledIf
|
|
|
|
|
|
|
|
|
|
`@DisabledIf` is used to signal that the annotated JUnit Jupiter test class or test
|
|
|
|
|
method is _disabled_ and should not be executed if the supplied `expression` evaluates to
|
|
|
|
|
`true`. Specifically, if the expression evaluates to `Boolean.TRUE` or a `String` equal
|
|
|
|
|
to `"true"` (ignoring case), the test will be __disabled__. When applied at the class
|
|
|
|
|
level, all test methods within that class are automatically disabled as well.
|
|
|
|
|
|
|
|
|
|
Expressions can be any of the following.
|
|
|
|
|
|
2017-09-28 02:05:42 +08:00
|
|
|
|
* <<core.adoc#expressions,Spring Expression Language>> (SpEL) expression – for example:
|
2017-09-27 22:25:16 +08:00
|
|
|
|
- `@DisabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")`
|
2017-09-28 02:05:42 +08:00
|
|
|
|
* Placeholder for a property available in the Spring
|
|
|
|
|
<<core.adoc#beans-environment,`Environment`>> – for example:
|
2017-09-27 22:25:16 +08:00
|
|
|
|
- `@DisabledIf("${smoke.tests.disabled}")`
|
|
|
|
|
* Text literal – for example:
|
|
|
|
|
- `@DisabledIf("true")`
|
|
|
|
|
|
|
|
|
|
Note, however, that a text literal which is _not_ the result of dynamic resolution of a
|
|
|
|
|
property placeholder is of zero practical value since `@DisabledIf("true")` is
|
|
|
|
|
equivalent to `@Disabled` and `@DisabledIf("false")` is logically meaningless.
|
|
|
|
|
|
|
|
|
|
`@DisabledIf` may be used as a meta-annotation to create custom composed annotations. For
|
|
|
|
|
example, a custom `@DisabledOnMac` annotation can be created as follows.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Target({ ElementType.TYPE, ElementType.METHOD })
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
|
|
|
@DisabledIf(
|
|
|
|
|
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
|
|
|
|
|
reason = "Disabled on Mac OS"
|
|
|
|
|
)
|
|
|
|
|
public @interface DisabledOnMac {}
|
|
|
|
|
----
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-meta]]
|
|
|
|
|
==== Meta-Annotation Support for Testing
|
2016-06-09 23:03:44 +08:00
|
|
|
|
It is possible to use most test-related annotations as
|
2017-03-29 20:20:12 +08:00
|
|
|
|
<<core.adoc#beans-meta-annotations,meta-annotations>> in order to create custom _composed
|
2016-06-09 23:03:44 +08:00
|
|
|
|
annotations_ and reduce configuration duplication across a test suite.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
Each of the following may be used as meta-annotations in conjunction with the
|
|
|
|
|
<<testcontext-framework,TestContext framework>>.
|
|
|
|
|
|
2016-06-10 00:55:36 +08:00
|
|
|
|
* `@BootstrapWith`
|
2015-03-03 18:38:01 +08:00
|
|
|
|
* `@ContextConfiguration`
|
|
|
|
|
* `@ContextHierarchy`
|
|
|
|
|
* `@ActiveProfiles`
|
|
|
|
|
* `@TestPropertySource`
|
|
|
|
|
* `@DirtiesContext`
|
|
|
|
|
* `@WebAppConfiguration`
|
|
|
|
|
* `@TestExecutionListeners`
|
|
|
|
|
* `@Transactional`
|
|
|
|
|
* `@BeforeTransaction`
|
|
|
|
|
* `@AfterTransaction`
|
2016-04-06 19:55:19 +08:00
|
|
|
|
* `@Commit`
|
2015-03-03 18:38:01 +08:00
|
|
|
|
* `@Rollback`
|
|
|
|
|
* `@Sql`
|
|
|
|
|
* `@SqlConfig`
|
|
|
|
|
* `@SqlGroup`
|
2017-09-28 03:04:58 +08:00
|
|
|
|
* `@Repeat` _(only supported on JUnit 4)_
|
|
|
|
|
* `@Timed` _(only supported on JUnit 4)_
|
|
|
|
|
* `@IfProfileValue` _(only supported on JUnit 4)_
|
|
|
|
|
* `@ProfileValueSourceConfiguration` _(only supported on JUnit 4)_
|
|
|
|
|
* `@SpringJUnitConfig` _(only supported on JUnit Jupiter)_
|
|
|
|
|
* `@SpringJUnitWebConfig` _(only supported on JUnit Jupiter)_
|
|
|
|
|
* `@EnabledIf` _(only supported on JUnit Jupiter)_
|
|
|
|
|
* `@DisabledIf` _(only supported on JUnit Jupiter)_
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-09-27 22:25:16 +08:00
|
|
|
|
For example, if we discover that we are repeating the following configuration across our
|
|
|
|
|
_JUnit 4_ based test suite...
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
|
|
|
|
|
@ActiveProfiles("dev")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@Transactional
|
|
|
|
|
public class OrderRepositoryTests { }
|
|
|
|
|
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
|
|
|
|
|
@ActiveProfiles("dev")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@Transactional
|
|
|
|
|
public class UserRepositoryTests { }
|
|
|
|
|
----
|
|
|
|
|
|
2017-09-27 22:25:16 +08:00
|
|
|
|
We can reduce the above duplication by introducing a custom _composed annotation_ that
|
|
|
|
|
centralizes the common test configuration for Spring like this:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Target(ElementType.TYPE)
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME)
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
|
|
|
|
|
@ActiveProfiles("dev")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@Transactional
|
2017-09-27 22:25:16 +08:00
|
|
|
|
public @interface TransactionalDevTestConfig { }
|
2015-03-03 18:38:01 +08:00
|
|
|
|
----
|
|
|
|
|
|
2017-09-27 22:25:16 +08:00
|
|
|
|
Then we can use our custom `@TransactionalDevTestConfig` annotation to simplify the
|
|
|
|
|
configuration of individual JUnit 4 based test classes as follows:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2017-09-27 22:25:16 +08:00
|
|
|
|
@TransactionalDevTestConfig
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class OrderRepositoryTests { }
|
|
|
|
|
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2017-09-27 22:25:16 +08:00
|
|
|
|
@TransactionalDevTestConfig
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class UserRepositoryTests { }
|
|
|
|
|
----
|
|
|
|
|
|
2017-09-27 22:25:16 +08:00
|
|
|
|
If we are writing tests using JUnit Jupiter, we can reduce code duplication even further
|
|
|
|
|
since annotations in JUnit 5 can also be used as meta-annotations. For example, if we
|
|
|
|
|
discover that we are repeating the following configuration across our JUnit Jupiter based
|
|
|
|
|
test suite...
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ExtendWith(SpringExtension.class)
|
|
|
|
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
|
|
|
|
|
@ActiveProfiles("dev")
|
|
|
|
|
@Transactional
|
|
|
|
|
class OrderRepositoryTests { }
|
|
|
|
|
|
|
|
|
|
@ExtendWith(SpringExtension.class)
|
|
|
|
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
|
|
|
|
|
@ActiveProfiles("dev")
|
|
|
|
|
@Transactional
|
|
|
|
|
class UserRepositoryTests { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
We can reduce the above duplication by introducing a custom _composed annotation_
|
|
|
|
|
that centralizes the common test configuration for Spring and JUnit Jupiter like this:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Target(ElementType.TYPE)
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
|
|
|
@ExtendWith(SpringExtension.class)
|
|
|
|
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
|
|
|
|
|
@ActiveProfiles("dev")
|
|
|
|
|
@Transactional
|
|
|
|
|
public @interface TransactionalDevTestConfig { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Then we can use our custom `@TransactionalDevTestConfig` annotation to simplify the
|
|
|
|
|
configuration of individual JUnit Jupiter based test classes as follows:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@TransactionalDevTestConfig
|
|
|
|
|
class OrderRepositoryTests { }
|
|
|
|
|
|
|
|
|
|
@TransactionalDevTestConfig
|
|
|
|
|
class UserRepositoryTests { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Since JUnit Jupiter supports the use of `@Test`, `@RepeatedTest`, `ParameterizedTest`,
|
|
|
|
|
etc. as meta-annotations, it is also possible to create custom composed annotations at
|
|
|
|
|
the test method level. For example, if we wish to create a _composed annotation_ that
|
|
|
|
|
combines the `@Test` and `@Tag` annotations from JUnit Jupiter with the `@Transactional`
|
|
|
|
|
annotation from Spring, we could create an `@TransactionalIntegrationTest` annotation as
|
|
|
|
|
follows.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Target(ElementType.METHOD)
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
|
|
|
@Transactional
|
|
|
|
|
@Tag("integration-test") // org.junit.jupiter.api.Tag
|
|
|
|
|
@Test // org.junit.jupiter.api.Test
|
|
|
|
|
public @interface TransactionalIntegrationTest { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Then we can use our custom `@TransactionalIntegrationTest` annotation to simplify the
|
|
|
|
|
configuration of individual JUnit Jupiter based test methods as follows:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@TransactionalIntegrationTest
|
|
|
|
|
void saveOrder() { }
|
|
|
|
|
|
|
|
|
|
@TransactionalIntegrationTest
|
|
|
|
|
void deleteOrder() { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
For further details, consult the <<core.adoc#annotation-programming-model,Spring
|
|
|
|
|
Annotation Programming Model>>.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-framework]]
|
|
|
|
|
=== Spring TestContext Framework
|
|
|
|
|
The __Spring TestContext Framework__ (located in the
|
|
|
|
|
`org.springframework.test.context` package) provides generic, annotation-driven unit and
|
|
|
|
|
integration testing support that is agnostic of the testing framework in use. The
|
|
|
|
|
TestContext framework also places a great deal of importance on __convention over
|
|
|
|
|
configuration__ with reasonable defaults that can be overridden through annotation-based
|
|
|
|
|
configuration.
|
|
|
|
|
|
|
|
|
|
In addition to generic testing infrastructure, the TestContext framework provides
|
2017-09-27 22:25:16 +08:00
|
|
|
|
explicit support for JUnit 4, JUnit Jupiter (a.k.a., JUnit 5), and TestNG. For JUnit 4
|
|
|
|
|
and TestNG, Spring provides `abstract` support classes. Furthermore, Spring provides a
|
|
|
|
|
custom JUnit `Runner` and custom JUnit `Rules` for _JUnit 4_ as well as a custom
|
|
|
|
|
`Extension` for _JUnit Jupiter_ that allow one to write so-called __POJO test classes__.
|
|
|
|
|
POJO test classes are not required to extend a particular class hierarchy such as the
|
|
|
|
|
`abstract` support classes.
|
|
|
|
|
|
|
|
|
|
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 interested in
|
|
|
|
|
_extending_ it with your own custom listeners or custom loaders, feel free to go directly
|
|
|
|
|
to the configuration (<<testcontext-ctx-management,context management>>,
|
2015-03-03 18:38:01 +08:00
|
|
|
|
<<testcontext-fixture-di,dependency injection>>, <<testcontext-tx,transaction
|
|
|
|
|
management>>), <<testcontext-support-classes,support classes>>, and
|
|
|
|
|
<<integration-testing-annotations,annotation support>> sections.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-key-abstractions]]
|
|
|
|
|
==== Key abstractions
|
2016-04-06 20:56:08 +08:00
|
|
|
|
The core of the framework consists of the `TestContextManager` class and the
|
|
|
|
|
`TestContext`, `TestExecutionListener`, and `SmartContextLoader` interfaces. A
|
|
|
|
|
`TestContextManager` is created per test class (e.g., for the execution of all test
|
2017-09-27 22:25:16 +08:00
|
|
|
|
methods within a single test class in JUnit Jupiter). The `TestContextManager` in turn
|
|
|
|
|
manages a `TestContext` that holds the context of the current test. The
|
|
|
|
|
`TestContextManager` also updates the state of the `TestContext` as the test progresses
|
|
|
|
|
and delegates to `TestExecutionListener` implementations, which instrument the actual
|
|
|
|
|
test execution by providing dependency injection, managing transactions, and so on. A
|
|
|
|
|
`SmartContextLoader` is responsible for loading an `ApplicationContext` for a given test
|
|
|
|
|
class. Consult the javadocs and the Spring test suite for further information and
|
|
|
|
|
examples of various implementations.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
===== TestContext
|
|
|
|
|
`TestContext` encapsulates the context in which a test is executed, agnostic of the
|
|
|
|
|
actual testing framework in use, and provides context management and caching support for
|
|
|
|
|
the test instance for which it is responsible. The `TestContext` also delegates to a
|
|
|
|
|
`SmartContextLoader` to load an `ApplicationContext` if requested.
|
|
|
|
|
|
|
|
|
|
===== TestContextManager
|
2016-09-05 23:48:47 +08:00
|
|
|
|
`TestContextManager` is the main entry point into the __Spring TestContext Framework__
|
|
|
|
|
and is responsible for managing a single `TestContext` and signaling events to each
|
|
|
|
|
registered `TestExecutionListener` at well-defined test execution points:
|
2016-06-09 23:03:44 +08:00
|
|
|
|
|
|
|
|
|
* prior to any __before class__ or __before all__ methods of a particular testing framework
|
|
|
|
|
* test instance post-processing
|
|
|
|
|
* prior to any __before__ or __before each__ methods of a particular testing framework
|
2016-09-05 23:48:47 +08:00
|
|
|
|
* immediately before execution of the test method but after test setup
|
|
|
|
|
* immediately after execution of the test method but before test tear down
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* after any __after__ or __after each__ methods of a particular testing framework
|
|
|
|
|
* after any __after class__ or __after all__ methods of a particular testing framework
|
|
|
|
|
|
|
|
|
|
===== TestExecutionListener
|
|
|
|
|
`TestExecutionListener` defines the API for reacting to test execution events published
|
|
|
|
|
by the `TestContextManager` with which the listener is registered. See
|
|
|
|
|
<<testcontext-tel-config>>.
|
|
|
|
|
|
|
|
|
|
===== Context Loaders
|
|
|
|
|
`ContextLoader` is a strategy interface that was introduced in Spring 2.5 for loading an
|
|
|
|
|
`ApplicationContext` for an integration test managed by the Spring TestContext Framework.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
Implement `SmartContextLoader` instead of this interface in order to provide support for
|
|
|
|
|
annotated classes, active bean definition profiles, test property sources, context
|
2016-06-09 22:01:13 +08:00
|
|
|
|
hierarchies, and `WebApplicationContext` support.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
`SmartContextLoader` is an extension of the `ContextLoader` interface introduced in
|
|
|
|
|
Spring 3.1. The `SmartContextLoader` SPI supersedes the `ContextLoader` SPI that was
|
|
|
|
|
introduced in Spring 2.5. Specifically, a `SmartContextLoader` can choose to process
|
|
|
|
|
resource `locations`, annotated `classes`, or context `initializers`. Furthermore, a
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`SmartContextLoader` can set active bean definition profiles and test property sources in
|
|
|
|
|
the context that it loads.
|
|
|
|
|
|
|
|
|
|
Spring provides the following implementations:
|
|
|
|
|
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* `DelegatingSmartContextLoader`: one of two default loaders which delegates internally
|
2015-03-03 18:38:01 +08:00
|
|
|
|
to an `AnnotationConfigContextLoader`, a `GenericXmlContextLoader`, or a
|
|
|
|
|
`GenericGroovyXmlContextLoader` depending either on the configuration declared for the
|
|
|
|
|
test class or on the presence of default locations or default configuration classes.
|
|
|
|
|
Groovy support is only enabled if Groovy is on the classpath.
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* `WebDelegatingSmartContextLoader`: one of two default loaders which delegates
|
2015-03-03 18:38:01 +08:00
|
|
|
|
internally to an `AnnotationConfigWebContextLoader`, a `GenericXmlWebContextLoader`, or a
|
|
|
|
|
`GenericGroovyXmlWebContextLoader` depending either on the configuration declared for the
|
|
|
|
|
test class or on the presence of default locations or default configuration classes. A
|
|
|
|
|
web `ContextLoader` will only be used if `@WebAppConfiguration` is present on the test
|
|
|
|
|
class. Groovy support is only enabled if Groovy is on the classpath.
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* `AnnotationConfigContextLoader`: loads a standard `ApplicationContext` from
|
2015-03-03 18:38:01 +08:00
|
|
|
|
__annotated classes__.
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* `AnnotationConfigWebContextLoader`: loads a `WebApplicationContext` from __annotated
|
2015-03-03 18:38:01 +08:00
|
|
|
|
classes__.
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* `GenericGroovyXmlContextLoader`: loads a standard `ApplicationContext` from __resource
|
2015-03-03 18:38:01 +08:00
|
|
|
|
locations__ that are either Groovy scripts or XML configuration files.
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* `GenericGroovyXmlWebContextLoader`: loads a `WebApplicationContext` from __resource
|
2015-03-03 18:38:01 +08:00
|
|
|
|
locations__ that are either Groovy scripts or XML configuration files.
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* `GenericXmlContextLoader`: loads a standard `ApplicationContext` from XML __resource
|
2015-03-03 18:38:01 +08:00
|
|
|
|
locations__.
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* `GenericXmlWebContextLoader`: loads a `WebApplicationContext` from XML __resource
|
2015-03-03 18:38:01 +08:00
|
|
|
|
locations__.
|
2016-06-09 23:03:44 +08:00
|
|
|
|
* `GenericPropertiesContextLoader`: loads a standard `ApplicationContext` from Java
|
2015-03-03 18:38:01 +08:00
|
|
|
|
Properties files.
|
|
|
|
|
|
2016-06-10 00:55:36 +08:00
|
|
|
|
[[testcontext-bootstrapping]]
|
|
|
|
|
==== Bootstrapping the TestContext framework
|
|
|
|
|
|
|
|
|
|
The default configuration for the internals of the Spring TestContext Framework is
|
|
|
|
|
sufficient for all common use cases. However, there are times when a development team or
|
|
|
|
|
third party framework would like to change the default `ContextLoader`, implement a
|
|
|
|
|
custom `TestContext` or `ContextCache`, augment the default sets of
|
|
|
|
|
`ContextCustomizerFactory` and `TestExecutionListener` implementations, etc. For such low
|
|
|
|
|
level control over how the TestContext framework operates, Spring provides a
|
|
|
|
|
bootstrapping strategy.
|
|
|
|
|
|
|
|
|
|
`TestContextBootstrapper` defines the SPI for _bootstrapping_ the TestContext framework.
|
|
|
|
|
A `TestContextBootstrapper` is used by the `TestContextManager` to load the
|
|
|
|
|
`TestExecutionListener` implementations for the current test and to build the
|
|
|
|
|
`TestContext` that it manages. A custom bootstrapping strategy can be configured for a
|
|
|
|
|
test class (or test class hierarchy) via `@BootstrapWith`, either directly or as a
|
|
|
|
|
meta-annotation. If a bootstrapper is not explicitly configured via `@BootstrapWith`,
|
|
|
|
|
either the `DefaultTestContextBootstrapper` or the `WebTestContextBootstrapper` will be
|
|
|
|
|
used, depending on the presence of `@WebAppConfiguration`.
|
|
|
|
|
|
|
|
|
|
Since the `TestContextBootstrapper` SPI is likely to change in the future in order to
|
|
|
|
|
accommodate new requirements, implementers are strongly encouraged not to implement this
|
|
|
|
|
interface directly but rather to extend `AbstractTestContextBootstrapper` or one of its
|
|
|
|
|
concrete subclasses instead.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[testcontext-tel-config]]
|
|
|
|
|
==== TestExecutionListener configuration
|
|
|
|
|
|
|
|
|
|
Spring provides the following `TestExecutionListener` implementations that are registered
|
|
|
|
|
by default, exactly in this order.
|
|
|
|
|
|
|
|
|
|
* `ServletTestExecutionListener`: configures Servlet API mocks for a
|
|
|
|
|
`WebApplicationContext`
|
2016-04-06 19:55:19 +08:00
|
|
|
|
* `DirtiesContextBeforeModesTestExecutionListener`: handles the `@DirtiesContext` annotation for
|
|
|
|
|
_before_ modes
|
2015-03-03 18:38:01 +08:00
|
|
|
|
* `DependencyInjectionTestExecutionListener`: provides dependency injection for the test
|
|
|
|
|
instance
|
2016-04-06 19:55:19 +08:00
|
|
|
|
* `DirtiesContextTestExecutionListener`: handles the `@DirtiesContext` annotation for
|
|
|
|
|
_after_ modes
|
2015-03-03 18:38:01 +08:00
|
|
|
|
* `TransactionalTestExecutionListener`: provides transactional test execution with
|
|
|
|
|
default rollback semantics
|
|
|
|
|
* `SqlScriptsTestExecutionListener`: executes SQL scripts configured via the `@Sql`
|
|
|
|
|
annotation
|
|
|
|
|
|
|
|
|
|
[[testcontext-tel-config-registering-tels]]
|
|
|
|
|
===== Registering custom TestExecutionListeners
|
|
|
|
|
|
2016-06-11 18:51:38 +08:00
|
|
|
|
Custom ``TestExecutionListener``s can be registered for a test class and its subclasses
|
2015-03-03 18:38:01 +08:00
|
|
|
|
via the `@TestExecutionListeners` annotation. See
|
|
|
|
|
<<integration-testing-annotations,annotation support>> and the javadocs for
|
|
|
|
|
`@TestExecutionListeners` for details and examples.
|
|
|
|
|
|
|
|
|
|
[[testcontext-tel-config-automatic-discovery]]
|
|
|
|
|
===== Automatic discovery of default TestExecutionListeners
|
|
|
|
|
|
2016-06-11 18:51:38 +08:00
|
|
|
|
Registering custom ``TestExecutionListener``s via `@TestExecutionListeners` is suitable
|
2015-03-03 18:38:01 +08:00
|
|
|
|
for custom listeners that are used in limited testing scenarios; however, it can become
|
2016-04-06 20:45:28 +08:00
|
|
|
|
cumbersome if a custom listener needs to be used across a test suite. Since Spring
|
|
|
|
|
Framework 4.1, this issue is addressed via support for automatic discovery of _default_
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`TestExecutionListener` implementations via the `SpringFactoriesLoader` mechanism.
|
|
|
|
|
|
|
|
|
|
Specifically, the `spring-test` module declares all core default
|
2016-06-11 18:51:38 +08:00
|
|
|
|
``TestExecutionListener``s under the
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`org.springframework.test.context.TestExecutionListener` key in its
|
|
|
|
|
`META-INF/spring.factories` properties file. Third-party frameworks and developers can
|
2016-06-11 18:51:38 +08:00
|
|
|
|
contribute their own ``TestExecutionListener``s to the list of default listeners in the
|
2015-03-03 18:38:01 +08:00
|
|
|
|
same manner via their own `META-INF/spring.factories` properties file.
|
|
|
|
|
|
|
|
|
|
[[testcontext-tel-config-ordering]]
|
|
|
|
|
===== Ordering TestExecutionListeners
|
|
|
|
|
|
2016-06-11 18:51:38 +08:00
|
|
|
|
When the TestContext framework discovers default ``TestExecutionListener``s via the
|
2015-03-03 18:38:01 +08:00
|
|
|
|
aforementioned `SpringFactoriesLoader` mechanism, the instantiated listeners are sorted
|
|
|
|
|
using Spring's `AnnotationAwareOrderComparator` which honors Spring's `Ordered` interface
|
|
|
|
|
and `@Order` annotation for ordering. `AbstractTestExecutionListener` and all default
|
2016-06-11 18:51:38 +08:00
|
|
|
|
``TestExecutionListener``s provided by Spring implement `Ordered` with appropriate
|
2015-03-03 18:38:01 +08:00
|
|
|
|
values. Third-party frameworks and developers should therefore make sure that their
|
2016-06-11 18:51:38 +08:00
|
|
|
|
_default_ ``TestExecutionListener``s are registered in the proper order by implementing
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`Ordered` or declaring `@Order`. Consult the javadocs for the `getOrder()` methods of the
|
2016-06-11 18:51:38 +08:00
|
|
|
|
core default ``TestExecutionListener``s for details on what values are assigned to each
|
2015-03-03 18:38:01 +08:00
|
|
|
|
core listener.
|
|
|
|
|
|
|
|
|
|
[[testcontext-tel-config-merging]]
|
|
|
|
|
===== Merging TestExecutionListeners
|
|
|
|
|
|
|
|
|
|
If a custom `TestExecutionListener` is registered via `@TestExecutionListeners`, the
|
|
|
|
|
_default_ listeners will not be registered. In most common testing scenarios, this
|
|
|
|
|
effectively forces the developer to manually declare all default listeners in addition to
|
|
|
|
|
any custom listeners. The following listing demonstrates this style of configuration.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestExecutionListeners({
|
|
|
|
|
MyCustomTestExecutionListener.class,
|
|
|
|
|
ServletTestExecutionListener.class,
|
2016-04-06 19:55:19 +08:00
|
|
|
|
DirtiesContextBeforeModesTestExecutionListener.class,
|
2015-03-03 18:38:01 +08:00
|
|
|
|
DependencyInjectionTestExecutionListener.class,
|
|
|
|
|
DirtiesContextTestExecutionListener.class,
|
|
|
|
|
TransactionalTestExecutionListener.class,
|
|
|
|
|
SqlScriptsTestExecutionListener.class
|
|
|
|
|
})
|
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
The challenge with this approach is that it requires that the developer know exactly
|
|
|
|
|
which listeners are registered by default. Moreover, the set of default listeners can
|
|
|
|
|
change from release to release -- for example, `SqlScriptsTestExecutionListener` was
|
2016-04-06 20:45:28 +08:00
|
|
|
|
introduced in Spring Framework 4.1, and `DirtiesContextBeforeModesTestExecutionListener`
|
|
|
|
|
was introduced in Spring Framework 4.2. Furthermore, third-party frameworks like Spring
|
2016-06-11 18:51:38 +08:00
|
|
|
|
Security register their own default ``TestExecutionListener``s via the aforementioned
|
2015-03-03 18:38:01 +08:00
|
|
|
|
<<testcontext-tel-config-automatic-discovery, automatic discovery mechanism>>.
|
|
|
|
|
|
|
|
|
|
To avoid having to be aware of and re-declare **all** _default_ listeners, the
|
|
|
|
|
`mergeMode` attribute of `@TestExecutionListeners` can be set to
|
|
|
|
|
`MergeMode.MERGE_WITH_DEFAULTS`. `MERGE_WITH_DEFAULTS` indicates that locally declared
|
|
|
|
|
listeners should be merged with the default listeners. The merging algorithm ensures that
|
|
|
|
|
duplicates are removed from the list and that the resulting set of merged listeners is
|
|
|
|
|
sorted according to the semantics of `AnnotationAwareOrderComparator` as described in
|
|
|
|
|
<<testcontext-tel-config-ordering>>. If a listener implements `Ordered` or is annotated
|
|
|
|
|
with `@Order` it can influence the position in which it is merged with the defaults;
|
|
|
|
|
otherwise, locally declared listeners will simply be appended to the list of default
|
|
|
|
|
listeners when merged.
|
|
|
|
|
|
|
|
|
|
For example, if the `MyCustomTestExecutionListener` class in the previous example
|
|
|
|
|
configures its `order` value (for example, `500`) to be less than the order of the
|
|
|
|
|
`ServletTestExecutionListener` (which happens to be `1000`), the
|
|
|
|
|
`MyCustomTestExecutionListener` can then be automatically merged with the list of
|
|
|
|
|
defaults _in front of_ the `ServletTestExecutionListener`, and the previous example could
|
|
|
|
|
be replaced with the following.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestExecutionListeners(
|
|
|
|
|
listeners = MyCustomTestExecutionListener.class,
|
2015-08-31 01:01:28 +08:00
|
|
|
|
mergeMode = MERGE_WITH_DEFAULTS
|
2015-03-03 18:38:01 +08:00
|
|
|
|
)
|
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management]]
|
|
|
|
|
==== Context management
|
|
|
|
|
|
|
|
|
|
Each `TestContext` provides context management and caching support for the test instance
|
|
|
|
|
it is responsible for. Test instances do not automatically receive access to the
|
|
|
|
|
configured `ApplicationContext`. However, if a test class implements the
|
|
|
|
|
`ApplicationContextAware` interface, a reference to the `ApplicationContext` is supplied
|
|
|
|
|
to the test instance. Note that `AbstractJUnit4SpringContextTests` and
|
|
|
|
|
`AbstractTestNGSpringContextTests` implement `ApplicationContextAware` and therefore
|
|
|
|
|
provide access to the `ApplicationContext` automatically.
|
|
|
|
|
|
|
|
|
|
.@Autowired ApplicationContext
|
|
|
|
|
[TIP]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
As an alternative to implementing the `ApplicationContextAware` interface, you can
|
|
|
|
|
inject the application context for your test class through the `@Autowired` annotation
|
|
|
|
|
on either a field or setter method. For example:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@ContextConfiguration
|
|
|
|
|
public class MyTest {
|
|
|
|
|
|
|
|
|
|
**@Autowired**
|
|
|
|
|
private ApplicationContext applicationContext;
|
|
|
|
|
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Similarly, if your test is configured to load a `WebApplicationContext`, you can inject
|
|
|
|
|
the web application context into your test as follows:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
**@WebAppConfiguration**
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
public class MyWebAppTest {
|
|
|
|
|
**@Autowired**
|
|
|
|
|
private WebApplicationContext wac;
|
|
|
|
|
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Dependency injection via `@Autowired` is provided by the
|
|
|
|
|
`DependencyInjectionTestExecutionListener` which is configured by default (see
|
|
|
|
|
<<testcontext-fixture-di>>).
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
Test classes that use the TestContext framework do not need to extend any particular
|
|
|
|
|
class or implement a specific interface to configure their application context. Instead,
|
|
|
|
|
configuration is achieved simply by declaring the `@ContextConfiguration` annotation at
|
|
|
|
|
the class level. If your test class does not explicitly declare application context
|
|
|
|
|
resource `locations` or annotated `classes`, the configured `ContextLoader` determines
|
|
|
|
|
how to load a context from a default location or default configuration classes. In
|
|
|
|
|
addition to context resource `locations` and annotated `classes`, an application context
|
|
|
|
|
can also be configured via application context `initializers`.
|
|
|
|
|
|
|
|
|
|
The following sections explain how to configure an `ApplicationContext` via XML
|
2016-04-06 20:36:36 +08:00
|
|
|
|
configuration files, Groovy scripts, annotated classes (typically `@Configuration`
|
|
|
|
|
classes), or context initializers using Spring's `@ContextConfiguration` annotation.
|
|
|
|
|
Alternatively, you can implement and configure your own custom `SmartContextLoader` for
|
|
|
|
|
advanced use cases.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-xml]]
|
|
|
|
|
===== Context configuration with XML resources
|
|
|
|
|
|
|
|
|
|
To load an `ApplicationContext` for your tests using XML configuration files, annotate
|
|
|
|
|
your test class with `@ContextConfiguration` and configure the `locations` attribute with
|
|
|
|
|
an array that contains the resource locations of XML configuration metadata. A plain or
|
|
|
|
|
relative path -- for example `"context.xml"` -- will be treated as a classpath resource
|
|
|
|
|
that is relative to the package in which the test class is defined. A path starting with
|
|
|
|
|
a slash is treated as an absolute classpath location, for example
|
|
|
|
|
`"/org/example/config.xml"`. A path which represents a resource URL (i.e., a path
|
|
|
|
|
prefixed with `classpath:`, `file:`, `http:`, etc.) will be used __as is__.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from "/app-config.xml" and
|
|
|
|
|
// "/test-config.xml" in the root of the classpath
|
|
|
|
|
**@ContextConfiguration(locations={"/app-config.xml", "/test-config.xml"})**
|
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
`@ContextConfiguration` supports an alias for the `locations` attribute through the
|
|
|
|
|
standard Java `value` attribute. Thus, if you do not need to declare additional
|
|
|
|
|
attributes in `@ContextConfiguration`, you can omit the declaration of the `locations`
|
|
|
|
|
attribute name and declare the resource locations by using the shorthand format
|
|
|
|
|
demonstrated in the following example.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2016-08-26 19:57:18 +08:00
|
|
|
|
**@ContextConfiguration({"/app-config.xml", "/test-config.xml"})**
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
If you omit both the `locations` and `value` attributes from the `@ContextConfiguration`
|
|
|
|
|
annotation, the TestContext framework will attempt to detect a default XML resource
|
|
|
|
|
location. Specifically, `GenericXmlContextLoader` and `GenericXmlWebContextLoader` detect
|
|
|
|
|
a default location based on the name of the test class. If your class is named
|
|
|
|
|
`com.example.MyTest`, `GenericXmlContextLoader` loads your application context from
|
|
|
|
|
`"classpath:com/example/MyTest-context.xml"`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
package com.example;
|
|
|
|
|
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from
|
|
|
|
|
// "classpath:com/example/MyTest-context.xml"
|
|
|
|
|
**@ContextConfiguration**
|
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-groovy]]
|
|
|
|
|
===== Context configuration with Groovy scripts
|
|
|
|
|
|
|
|
|
|
To load an `ApplicationContext` for your tests using Groovy scripts that utilize the
|
2017-03-29 20:20:12 +08:00
|
|
|
|
<<core.adoc#groovy-bean-definition-dsl,Groovy Bean Definition DSL>>, annotate your test class with
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`@ContextConfiguration` and configure the `locations` or `value` attribute with an array
|
|
|
|
|
that contains the resource locations of Groovy scripts. Resource lookup semantics for
|
|
|
|
|
Groovy scripts are the same as those described for <<testcontext-ctx-management-xml,XML
|
|
|
|
|
configuration files>>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.Enabling Groovy script support
|
|
|
|
|
[TIP]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
Support for using Groovy scripts to load an `ApplicationContext` in the Spring
|
|
|
|
|
TestContext Framework is enabled automatically if Groovy is on the classpath.
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from "/AppConfig.groovy" and
|
|
|
|
|
// "/TestConfig.groovy" in the root of the classpath
|
2016-08-26 19:57:18 +08:00
|
|
|
|
**@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"})**
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
If you omit both the `locations` and `value` attributes from the `@ContextConfiguration`
|
|
|
|
|
annotation, the TestContext framework will attempt to detect a default Groovy script.
|
|
|
|
|
Specifically, `GenericGroovyXmlContextLoader` and `GenericGroovyXmlWebContextLoader`
|
|
|
|
|
detect a default location based on the name of the test class. If your class is named
|
|
|
|
|
`com.example.MyTest`, the Groovy context loader will load your application context from
|
|
|
|
|
`"classpath:com/example/MyTestContext.groovy"`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
package com.example;
|
|
|
|
|
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from
|
|
|
|
|
// "classpath:com/example/MyTestContext.groovy"
|
|
|
|
|
**@ContextConfiguration**
|
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
.Declaring XML config and Groovy scripts simultaneously
|
|
|
|
|
[TIP]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
Both XML configuration files and Groovy scripts can be declared simultaneously via the
|
|
|
|
|
`locations` or `value` attribute of `@ContextConfiguration`. If the path to a configured
|
|
|
|
|
resource location ends with `.xml` it will be loaded using an `XmlBeanDefinitionReader`;
|
|
|
|
|
otherwise it will be loaded using a `GroovyBeanDefinitionReader`.
|
|
|
|
|
|
|
|
|
|
The following listing demonstrates how to combine both in an integration test.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from
|
|
|
|
|
// "/app-config.xml" and "/TestConfig.groovy"
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration({ "/app-config.xml", "/TestConfig.groovy" })
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-javaconfig]]
|
|
|
|
|
===== Context configuration with annotated classes
|
|
|
|
|
|
|
|
|
|
To load an `ApplicationContext` for your tests using __annotated classes__ (see
|
2017-03-29 20:20:12 +08:00
|
|
|
|
<<core.adoc#beans-java, Java-based container configuration>>),
|
|
|
|
|
annotate your test class with `@ContextConfiguration` and configure the
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`classes` attribute with an array that contains references to annotated classes.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from AppConfig and TestConfig
|
|
|
|
|
**@ContextConfiguration(classes = {AppConfig.class, TestConfig.class})**
|
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
.Annotated Classes
|
|
|
|
|
[TIP]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
The term __annotated class__ can refer to any of the following.
|
|
|
|
|
|
|
|
|
|
* A class annotated with `@Configuration`
|
|
|
|
|
* A component (i.e., a class annotated with `@Component`, `@Service`, `@Repository`, etc.)
|
|
|
|
|
* A JSR-330 compliant class that is annotated with `javax.inject` annotations
|
|
|
|
|
* Any other class that contains `@Bean`-methods
|
|
|
|
|
|
|
|
|
|
Consult the javadocs of `@Configuration` and `@Bean` for further information regarding
|
|
|
|
|
the configuration and semantics of __annotated classes__, paying special attention to
|
|
|
|
|
the discussion of __`@Bean` Lite Mode__.
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
If you omit the `classes` attribute from the `@ContextConfiguration` annotation, the
|
|
|
|
|
TestContext framework will attempt to detect the presence of default configuration
|
|
|
|
|
classes. Specifically, `AnnotationConfigContextLoader` and
|
|
|
|
|
`AnnotationConfigWebContextLoader` will detect all `static` nested classes of the test class
|
|
|
|
|
that meet the requirements for configuration class implementations as specified in the
|
|
|
|
|
`@Configuration` javadocs. In the following example, the `OrderServiceTest` class
|
|
|
|
|
declares a `static` nested configuration class named `Config` that will be automatically
|
|
|
|
|
used to load the `ApplicationContext` for the test class. Note that the name of the
|
|
|
|
|
configuration class is arbitrary. In addition, a test class can contain more than one
|
|
|
|
|
`static` nested configuration class if desired.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from the
|
|
|
|
|
// static nested Config class
|
|
|
|
|
**@ContextConfiguration**
|
|
|
|
|
public class OrderServiceTest {
|
|
|
|
|
|
|
|
|
|
@Configuration
|
|
|
|
|
static class Config {
|
|
|
|
|
|
|
|
|
|
// this bean will be injected into the OrderServiceTest class
|
|
|
|
|
@Bean
|
|
|
|
|
public OrderService orderService() {
|
|
|
|
|
OrderService orderService = new OrderServiceImpl();
|
|
|
|
|
// set properties, etc.
|
|
|
|
|
return orderService;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private OrderService orderService;
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testOrderService() {
|
|
|
|
|
// test the orderService
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-mixed-config]]
|
|
|
|
|
===== Mixing XML, Groovy scripts, and annotated classes
|
|
|
|
|
|
|
|
|
|
It may sometimes be desirable to mix XML configuration files, Groovy scripts, and
|
|
|
|
|
annotated classes (i.e., typically `@Configuration` classes) to configure an
|
|
|
|
|
`ApplicationContext` for your tests. For example, if you use XML configuration in
|
|
|
|
|
production, you may decide that you want to use `@Configuration` classes to configure
|
|
|
|
|
specific Spring-managed components for your tests, or vice versa.
|
|
|
|
|
|
|
|
|
|
Furthermore, some third-party frameworks (like Spring Boot) provide first-class support
|
|
|
|
|
for loading an `ApplicationContext` from different types of resources simultaneously
|
|
|
|
|
(e.g., XML configuration files, Groovy scripts, and `@Configuration` classes). The Spring
|
|
|
|
|
Framework historically has not supported this for standard deployments. Consequently,
|
|
|
|
|
most of the `SmartContextLoader` implementations that the Spring Framework delivers in
|
|
|
|
|
the `spring-test` module support only one resource type per test context; however, this
|
|
|
|
|
does not mean that you cannot use both. One exception to the general rule is that the
|
|
|
|
|
`GenericGroovyXmlContextLoader` and `GenericGroovyXmlWebContextLoader` support both XML
|
|
|
|
|
configuration files and Groovy scripts simultaneously. Furthermore, third-party
|
|
|
|
|
frameworks may choose to support the declaration of both `locations` and `classes` via
|
|
|
|
|
`@ContextConfiguration`, and with the standard testing support in the TestContext
|
|
|
|
|
framework, you have the following options.
|
|
|
|
|
|
|
|
|
|
If you want to use resource locations (e.g., XML or Groovy) __and__ `@Configuration`
|
|
|
|
|
classes to configure your tests, you will have to pick one as the __entry point__, and
|
|
|
|
|
that one will have to include or import the other. For example, in XML or Groovy scripts
|
|
|
|
|
you can include `@Configuration` classes via component scanning or define them as normal
|
|
|
|
|
Spring beans; whereas, in a `@Configuration` class you can use `@ImportResource` to
|
2016-04-06 20:36:36 +08:00
|
|
|
|
import XML configuration files or Groovy scripts. Note that this behavior is semantically
|
|
|
|
|
equivalent to how you configure your application in production: in production
|
|
|
|
|
configuration you will define either a set of XML or Groovy resource locations or a set
|
|
|
|
|
of `@Configuration` classes that your production `ApplicationContext` will be loaded
|
|
|
|
|
from, but you still have the freedom to include or import the other type of configuration.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-initializers]]
|
|
|
|
|
===== Context configuration with context initializers
|
|
|
|
|
To configure an `ApplicationContext` for your tests using context initializers, annotate
|
|
|
|
|
your test class with `@ContextConfiguration` and configure the `initializers` attribute
|
|
|
|
|
with an array that contains references to classes that implement
|
|
|
|
|
`ApplicationContextInitializer`. The declared context initializers will then be used to
|
|
|
|
|
initialize the `ConfigurableApplicationContext` that is loaded for your tests. Note that
|
|
|
|
|
the concrete `ConfigurableApplicationContext` type supported by each declared
|
|
|
|
|
initializer must be compatible with the type of `ApplicationContext` created by the
|
|
|
|
|
`SmartContextLoader` in use (i.e., typically a `GenericApplicationContext`).
|
|
|
|
|
Furthermore, the order in which the initializers are invoked depends on whether they
|
2016-04-06 20:20:05 +08:00
|
|
|
|
implement Spring's `Ordered` interface or are annotated with Spring's `@Order` annotation
|
|
|
|
|
or the standard `@Priority` annotation.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from TestConfig
|
|
|
|
|
// and initialized by TestAppCtxInitializer
|
|
|
|
|
**@ContextConfiguration(
|
|
|
|
|
classes = TestConfig.class,
|
|
|
|
|
initializers = TestAppCtxInitializer.class)**
|
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2016-04-06 20:36:36 +08:00
|
|
|
|
It is also possible to omit the declaration of XML configuration files, Groovy scripts,
|
|
|
|
|
or annotated classes in `@ContextConfiguration` entirely and instead declare only
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`ApplicationContextInitializer` classes which are then responsible for registering beans
|
|
|
|
|
in the context -- for example, by programmatically loading bean definitions from XML
|
|
|
|
|
files or configuration classes.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be initialized by EntireAppInitializer
|
|
|
|
|
// which presumably registers beans in the context
|
|
|
|
|
**@ContextConfiguration(initializers = EntireAppInitializer.class)**
|
|
|
|
|
public class MyTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-inheritance]]
|
|
|
|
|
===== Context configuration inheritance
|
|
|
|
|
`@ContextConfiguration` supports boolean `inheritLocations` and `inheritInitializers`
|
|
|
|
|
attributes that denote whether resource locations or annotated classes and context
|
|
|
|
|
initializers declared by superclasses should be __inherited__. The default value for
|
|
|
|
|
both flags is `true`. This means that a test class inherits the resource locations or
|
|
|
|
|
annotated classes as well as the context initializers declared by any superclasses.
|
|
|
|
|
Specifically, the resource locations or annotated classes for a test class are appended
|
|
|
|
|
to the list of resource locations or annotated classes declared by superclasses.
|
|
|
|
|
Similarly, the initializers for a given test class will be added to the set of
|
|
|
|
|
initializers defined by test superclasses. Thus, subclasses have the option
|
|
|
|
|
of __extending__ the resource locations, annotated classes, or context initializers.
|
|
|
|
|
|
2015-06-21 22:25:00 +08:00
|
|
|
|
If the `inheritLocations` or `inheritInitializers` attribute in `@ContextConfiguration`
|
|
|
|
|
is set to `false`, the resource locations or annotated classes and the context
|
2015-03-03 18:38:01 +08:00
|
|
|
|
initializers, respectively, for the test class __shadow__ and effectively replace the
|
|
|
|
|
configuration defined by superclasses.
|
|
|
|
|
|
|
|
|
|
In the following example that uses XML resource locations, the `ApplicationContext` for
|
|
|
|
|
`ExtendedTest` will be loaded from __"base-config.xml"__ __and__
|
|
|
|
|
__"extended-config.xml"__, in that order. Beans defined in __"extended-config.xml"__ may
|
|
|
|
|
therefore __override__ (i.e., replace) those defined in __"base-config.xml"__.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from "/base-config.xml"
|
|
|
|
|
// in the root of the classpath
|
2016-08-26 19:57:18 +08:00
|
|
|
|
**@ContextConfiguration("/base-config.xml")**
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class BaseTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ApplicationContext will be loaded from "/base-config.xml" and
|
|
|
|
|
// "/extended-config.xml" in the root of the classpath
|
2016-08-26 19:57:18 +08:00
|
|
|
|
**@ContextConfiguration("/extended-config.xml")**
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class ExtendedTest extends BaseTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Similarly, in the following example that uses annotated classes, the
|
|
|
|
|
`ApplicationContext` for `ExtendedTest` will be loaded from the `BaseConfig` __and__
|
|
|
|
|
`ExtendedConfig` classes, in that order. Beans defined in `ExtendedConfig` may therefore
|
|
|
|
|
override (i.e., replace) those defined in `BaseConfig`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from BaseConfig
|
|
|
|
|
**@ContextConfiguration(classes = BaseConfig.class)**
|
|
|
|
|
public class BaseTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig
|
|
|
|
|
**@ContextConfiguration(classes = ExtendedConfig.class)**
|
|
|
|
|
public class ExtendedTest extends BaseTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
In the following example that uses context initializers, the `ApplicationContext` for
|
|
|
|
|
`ExtendedTest` will be initialized using `BaseInitializer` __and__
|
|
|
|
|
`ExtendedInitializer`. Note, however, that the order in which the initializers are
|
2016-04-06 20:20:05 +08:00
|
|
|
|
invoked depends on whether they implement Spring's `Ordered` interface or are annotated
|
|
|
|
|
with Spring's `@Order` annotation or the standard `@Priority` annotation.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be initialized by BaseInitializer
|
|
|
|
|
**@ContextConfiguration(initializers = BaseInitializer.class)**
|
|
|
|
|
public class BaseTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ApplicationContext will be initialized by BaseInitializer
|
|
|
|
|
// and ExtendedInitializer
|
|
|
|
|
**@ContextConfiguration(initializers = ExtendedInitializer.class)**
|
|
|
|
|
public class ExtendedTest extends BaseTest {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-env-profiles]]
|
|
|
|
|
===== Context configuration with environment profiles
|
|
|
|
|
Spring 3.1 introduced first-class support in the framework for the notion of
|
|
|
|
|
environments and profiles (a.k.a., __bean definition profiles__), and integration tests
|
|
|
|
|
can be configured to activate particular bean definition profiles for various testing
|
|
|
|
|
scenarios. This is achieved by annotating a test class with the `@ActiveProfiles`
|
|
|
|
|
annotation and supplying a list of profiles that should be activated when loading the
|
|
|
|
|
`ApplicationContext` for the test.
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`@ActiveProfiles` may be used with any implementation of the new `SmartContextLoader`
|
|
|
|
|
SPI, but `@ActiveProfiles` is not supported with implementations of the older
|
|
|
|
|
`ContextLoader` SPI.
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
Let's take a look at some examples with XML configuration and `@Configuration` classes.
|
|
|
|
|
|
|
|
|
|
[source,xml,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
<!-- app-config.xml -->
|
|
|
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
|
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
|
|
|
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
|
|
|
|
|
xmlns:jee="http://www.springframework.org/schema/jee"
|
|
|
|
|
xsi:schemaLocation="...">
|
|
|
|
|
|
|
|
|
|
<bean id="transferService"
|
|
|
|
|
class="com.bank.service.internal.DefaultTransferService">
|
|
|
|
|
<constructor-arg ref="accountRepository"/>
|
|
|
|
|
<constructor-arg ref="feePolicy"/>
|
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
|
|
<bean id="accountRepository"
|
|
|
|
|
class="com.bank.repository.internal.JdbcAccountRepository">
|
|
|
|
|
<constructor-arg ref="dataSource"/>
|
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
|
|
<bean id="feePolicy"
|
|
|
|
|
class="com.bank.service.internal.ZeroFeePolicy"/>
|
|
|
|
|
|
|
|
|
|
<beans profile="dev">
|
|
|
|
|
<jdbc:embedded-database id="dataSource">
|
|
|
|
|
<jdbc:script
|
|
|
|
|
location="classpath:com/bank/config/sql/schema.sql"/>
|
|
|
|
|
<jdbc:script
|
|
|
|
|
location="classpath:com/bank/config/sql/test-data.sql"/>
|
|
|
|
|
</jdbc:embedded-database>
|
|
|
|
|
</beans>
|
|
|
|
|
|
|
|
|
|
<beans profile="production">
|
|
|
|
|
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
|
|
|
|
|
</beans>
|
|
|
|
|
|
|
|
|
|
<beans profile="default">
|
|
|
|
|
<jdbc:embedded-database id="dataSource">
|
|
|
|
|
<jdbc:script
|
|
|
|
|
location="classpath:com/bank/config/sql/schema.sql"/>
|
|
|
|
|
</jdbc:embedded-database>
|
|
|
|
|
</beans>
|
|
|
|
|
|
|
|
|
|
</beans>
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
package com.bank.service;
|
|
|
|
|
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// ApplicationContext will be loaded from "classpath:/app-config.xml"
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration("/app-config.xml")
|
|
|
|
|
@ActiveProfiles("dev")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class TransferServiceTest {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private TransferService transferService;
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testTransferService() {
|
|
|
|
|
// test the transferService
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
When `TransferServiceTest` is run, its `ApplicationContext` will be loaded from the
|
|
|
|
|
`app-config.xml` configuration file in the root of the classpath. If you inspect
|
|
|
|
|
`app-config.xml` you'll notice that the `accountRepository` bean has a dependency on a
|
|
|
|
|
`dataSource` bean; however, `dataSource` is not defined as a top-level bean. Instead,
|
|
|
|
|
`dataSource` is defined three times: in the __production__ profile, the
|
|
|
|
|
__dev__ profile, and the __default__ profile.
|
|
|
|
|
|
2016-08-26 19:57:18 +08:00
|
|
|
|
By annotating `TransferServiceTest` with `@ActiveProfiles("dev")` we instruct the Spring
|
2015-03-03 18:38:01 +08:00
|
|
|
|
TestContext Framework to load the `ApplicationContext` with the active profiles set to
|
|
|
|
|
`{"dev"}`. As a result, an embedded database will be created and populated with test data,
|
|
|
|
|
and the `accountRepository` bean will be wired with a reference to the development
|
|
|
|
|
`DataSource`. And that's likely what we want in an integration test.
|
|
|
|
|
|
|
|
|
|
It is sometimes useful to assign beans to a `default` profile. Beans within the default profile
|
|
|
|
|
are only included when no other profile is specifically activated. This can be used to define
|
|
|
|
|
_fallback_ beans to be used in the application's default state. For example, you may
|
|
|
|
|
explicitly provide a data source for `dev` and `production` profiles, but define an in-memory
|
|
|
|
|
data source as a default when neither of these is active.
|
|
|
|
|
|
|
|
|
|
The following code listings demonstrate how to implement the same configuration and
|
|
|
|
|
integration test but using `@Configuration` classes instead of XML.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Configuration
|
|
|
|
|
@Profile("dev")
|
|
|
|
|
public class StandaloneDataConfig {
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public DataSource dataSource() {
|
|
|
|
|
return new EmbeddedDatabaseBuilder()
|
|
|
|
|
.setType(EmbeddedDatabaseType.HSQL)
|
|
|
|
|
.addScript("classpath:com/bank/config/sql/schema.sql")
|
|
|
|
|
.addScript("classpath:com/bank/config/sql/test-data.sql")
|
|
|
|
|
.build();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Configuration
|
|
|
|
|
@Profile("production")
|
|
|
|
|
public class JndiDataConfig {
|
|
|
|
|
|
|
|
|
|
@Bean(destroyMethod="")
|
|
|
|
|
public DataSource dataSource() throws Exception {
|
|
|
|
|
Context ctx = new InitialContext();
|
|
|
|
|
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Configuration
|
|
|
|
|
@Profile("default")
|
|
|
|
|
public class DefaultDataConfig {
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public DataSource dataSource() {
|
|
|
|
|
return new EmbeddedDatabaseBuilder()
|
|
|
|
|
.setType(EmbeddedDatabaseType.HSQL)
|
|
|
|
|
.addScript("classpath:com/bank/config/sql/schema.sql")
|
|
|
|
|
.build();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Configuration
|
|
|
|
|
public class TransferServiceConfig {
|
|
|
|
|
|
|
|
|
|
@Autowired DataSource dataSource;
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public TransferService transferService() {
|
|
|
|
|
return new DefaultTransferService(accountRepository(), feePolicy());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public AccountRepository accountRepository() {
|
|
|
|
|
return new JdbcAccountRepository(dataSource);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public FeePolicy feePolicy() {
|
|
|
|
|
return new ZeroFeePolicy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
package com.bank.service;
|
|
|
|
|
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@ContextConfiguration(classes = {
|
|
|
|
|
TransferServiceConfig.class,
|
|
|
|
|
StandaloneDataConfig.class,
|
|
|
|
|
JndiDataConfig.class,
|
|
|
|
|
DefaultDataConfig.class})
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ActiveProfiles("dev")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class TransferServiceTest {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private TransferService transferService;
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testTransferService() {
|
|
|
|
|
// test the transferService
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
In this variation, we have split the XML configuration into four independent
|
|
|
|
|
`@Configuration` classes:
|
|
|
|
|
|
|
|
|
|
* `TransferServiceConfig`: acquires a `dataSource` via dependency injection using
|
|
|
|
|
`@Autowired`
|
|
|
|
|
* `StandaloneDataConfig`: defines a `dataSource` for an embedded database suitable for
|
|
|
|
|
developer tests
|
|
|
|
|
* `JndiDataConfig`: defines a `dataSource` that is retrieved from JNDI in a production
|
|
|
|
|
environment
|
|
|
|
|
* `DefaultDataConfig`: defines a `dataSource` for a default embedded database in case
|
|
|
|
|
no profile is active
|
|
|
|
|
|
|
|
|
|
As with the XML-based configuration example, we still annotate `TransferServiceTest`
|
2016-08-26 19:57:18 +08:00
|
|
|
|
with `@ActiveProfiles("dev")`, but this time we specify all four configuration classes
|
2015-03-03 18:38:01 +08:00
|
|
|
|
via the `@ContextConfiguration` annotation. The body of the test class itself remains
|
|
|
|
|
completely unchanged.
|
|
|
|
|
|
|
|
|
|
It is often the case that a single set of profiles is used across multiple test classes
|
|
|
|
|
within a given project. Thus, to avoid duplicate declarations of the `@ActiveProfiles`
|
|
|
|
|
annotation it is possible to declare `@ActiveProfiles` once on a base class, and
|
|
|
|
|
subclasses will automatically inherit the `@ActiveProfiles` configuration from the base
|
|
|
|
|
class. In the following example, the declaration of `@ActiveProfiles` (as well as other
|
|
|
|
|
annotations) has been moved to an abstract superclass, `AbstractIntegrationTest`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
package com.bank.service;
|
|
|
|
|
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@ContextConfiguration(classes = {
|
|
|
|
|
TransferServiceConfig.class,
|
|
|
|
|
StandaloneDataConfig.class,
|
|
|
|
|
JndiDataConfig.class,
|
|
|
|
|
DefaultDataConfig.class})
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ActiveProfiles("dev")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public abstract class AbstractIntegrationTest {
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
package com.bank.service;
|
|
|
|
|
|
|
|
|
|
// "dev" profile inherited from superclass
|
|
|
|
|
public class TransferServiceTest extends AbstractIntegrationTest {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private TransferService transferService;
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testTransferService() {
|
|
|
|
|
// test the transferService
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
`@ActiveProfiles` also supports an `inheritProfiles` attribute that can be used to
|
|
|
|
|
disable the inheritance of active profiles.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
package com.bank.service;
|
|
|
|
|
|
|
|
|
|
// "dev" profile overridden with "production"
|
|
|
|
|
@ActiveProfiles(profiles = "production", inheritProfiles = false)
|
|
|
|
|
public class ProductionTransferServiceTest extends AbstractIntegrationTest {
|
|
|
|
|
// test body
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-env-profiles-ActiveProfilesResolver]]
|
|
|
|
|
Furthermore, it is sometimes necessary to resolve active profiles for tests
|
|
|
|
|
__programmatically__ instead of declaratively -- for example, based on:
|
|
|
|
|
|
|
|
|
|
* the current operating system
|
|
|
|
|
* whether tests are being executed on a continuous integration build server
|
|
|
|
|
* the presence of certain environment variables
|
|
|
|
|
* the presence of custom class-level annotations
|
|
|
|
|
* etc.
|
|
|
|
|
|
|
|
|
|
To resolve active bean definition profiles programmatically, simply implement a custom
|
|
|
|
|
`ActiveProfilesResolver` and register it via the `resolver` attribute of
|
|
|
|
|
`@ActiveProfiles`. The following example demonstrates how to implement and register a
|
|
|
|
|
custom `OperatingSystemActiveProfilesResolver`. For further information, refer to the
|
|
|
|
|
corresponding javadocs.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
package com.bank.service;
|
|
|
|
|
|
|
|
|
|
// "dev" profile overridden programmatically via a custom resolver
|
|
|
|
|
@ActiveProfiles(
|
|
|
|
|
resolver = OperatingSystemActiveProfilesResolver.class,
|
|
|
|
|
inheritProfiles = false)
|
|
|
|
|
public class TransferServiceTest extends AbstractIntegrationTest {
|
|
|
|
|
// test body
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
package com.bank.service.test;
|
|
|
|
|
|
|
|
|
|
public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
String[] resolve(Class<?> testClass) {
|
|
|
|
|
String profile = ...;
|
|
|
|
|
// determine the value of profile based on the operating system
|
|
|
|
|
return new String[] {profile};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-property-sources]]
|
|
|
|
|
===== Context configuration with test property sources
|
|
|
|
|
|
|
|
|
|
Spring 3.1 introduced first-class support in the framework for the notion of an
|
|
|
|
|
environment with a hierarchy of _property sources_, and since Spring 4.1 integration
|
|
|
|
|
tests can be configured with test-specific property sources. In contrast to the
|
|
|
|
|
`@PropertySource` annotation used on `@Configuration` classes, the `@TestPropertySource`
|
|
|
|
|
annotation can be declared on a test class to declare resource locations for test
|
|
|
|
|
properties files or _inlined_ properties. These test property sources will be added to
|
2015-06-21 22:25:00 +08:00
|
|
|
|
the set of `PropertySources` in the `Environment` for the `ApplicationContext` loaded
|
|
|
|
|
for the annotated integration test.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[NOTE]
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`@TestPropertySource` may be used with any implementation of the `SmartContextLoader`
|
|
|
|
|
SPI, but `@TestPropertySource` is not supported with implementations of the older
|
|
|
|
|
`ContextLoader` SPI.
|
|
|
|
|
|
|
|
|
|
Implementations of `SmartContextLoader` gain access to merged test property source values
|
|
|
|
|
via the `getPropertySourceLocations()` and `getPropertySourceProperties()` methods in
|
|
|
|
|
`MergedContextConfiguration`.
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
*Declaring test property sources*
|
|
|
|
|
|
|
|
|
|
Test properties files can be configured via the `locations` or `value` attribute of
|
|
|
|
|
`@TestPropertySource` as shown in the following example.
|
|
|
|
|
|
|
|
|
|
Both traditional and XML-based properties file formats are supported -- for example,
|
|
|
|
|
`"classpath:/com/example/test.properties"` or `"file:///path/to/file.xml"`.
|
|
|
|
|
|
|
|
|
|
Each path will be interpreted as a Spring `Resource`. A plain path -- for example,
|
|
|
|
|
`"test.properties"` -- will be treated as a classpath resource that is _relative_ to the
|
|
|
|
|
package in which the test class is defined. A path starting with a slash will be treated
|
|
|
|
|
as an _absolute_ classpath resource, for example: `"/org/example/test.xml"`. A path which
|
|
|
|
|
references a URL (e.g., a path prefixed with `classpath:`, `file:`, `http:`, etc.) will
|
|
|
|
|
be loaded using the specified resource protocol. Resource location wildcards (e.g.
|
|
|
|
|
`**/*.properties`) are not permitted: each location must evaluate to exactly one
|
|
|
|
|
`.properties` or `.xml` resource.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@TestPropertySource("/test.properties")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class MyIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
_Inlined_ properties in the form of key-value pairs can be configured via the
|
|
|
|
|
`properties` attribute of `@TestPropertySource` as shown in the following example. All
|
|
|
|
|
key-value pairs will be added to the enclosing `Environment` as a single test
|
|
|
|
|
`PropertySource` with the highest precedence.
|
|
|
|
|
|
|
|
|
|
The supported syntax for key-value pairs is the same as the syntax defined for entries in
|
|
|
|
|
a Java properties file:
|
|
|
|
|
|
|
|
|
|
* `"key=value"`
|
|
|
|
|
* `"key:value"`
|
|
|
|
|
* `"key value"`
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"})
|
|
|
|
|
public class MyIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
*Default properties file detection*
|
|
|
|
|
|
|
|
|
|
If `@TestPropertySource` is declared as an empty annotation (i.e., without explicit
|
|
|
|
|
values for the `locations` or `properties` attributes), an attempt will be made to detect
|
|
|
|
|
a _default_ properties file relative to the class that declared the annotation. For
|
|
|
|
|
example, if the annotated test class is `com.example.MyTest`, the corresponding default
|
|
|
|
|
properties file is `"classpath:com/example/MyTest.properties"`. If the default cannot be
|
|
|
|
|
detected, an `IllegalStateException` will be thrown.
|
|
|
|
|
|
|
|
|
|
*Precedence*
|
|
|
|
|
|
|
|
|
|
Test property sources have higher precedence than those loaded from the operating
|
|
|
|
|
system's environment or Java system properties as well as property sources added by the
|
|
|
|
|
application declaratively via `@PropertySource` or programmatically. Thus, test property
|
|
|
|
|
sources can be used to selectively override properties defined in system and application
|
|
|
|
|
property sources. Furthermore, inlined properties have higher precedence than properties
|
|
|
|
|
loaded from resource locations.
|
|
|
|
|
|
|
|
|
|
In the following example, the `timezone` and `port` properties as well as any properties
|
|
|
|
|
defined in `"/test.properties"` will override any properties of the same name that are
|
|
|
|
|
defined in system and application property sources. Furthermore, if the
|
|
|
|
|
`"/test.properties"` file defines entries for the `timezone` and `port` properties those
|
|
|
|
|
will be overridden by the _inlined_ properties declared via the `properties` attribute.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestPropertySource(
|
|
|
|
|
locations = "/test.properties",
|
|
|
|
|
properties = {"timezone = GMT", "port: 4242"}
|
|
|
|
|
)
|
|
|
|
|
public class MyIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
*Inheriting and overriding test property sources*
|
|
|
|
|
|
|
|
|
|
`@TestPropertySource` supports boolean `inheritLocations` and `inheritProperties`
|
|
|
|
|
attributes that denote whether resource locations for properties files and inlined
|
|
|
|
|
properties declared by superclasses should be __inherited__. The default value for both
|
|
|
|
|
flags is `true`. This means that a test class inherits the locations and inlined
|
|
|
|
|
properties declared by any superclasses. Specifically, the locations and inlined
|
|
|
|
|
properties for a test class are appended to the locations and inlined properties declared
|
|
|
|
|
by superclasses. Thus, subclasses have the option of __extending__ the locations and
|
|
|
|
|
inlined properties. Note that properties that appear later will __shadow__ (i.e..,
|
|
|
|
|
override) properties of the same name that appear earlier. In addition, the
|
|
|
|
|
aforementioned precedence rules apply for inherited test property sources as well.
|
|
|
|
|
|
2015-06-21 22:25:00 +08:00
|
|
|
|
If the `inheritLocations` or `inheritProperties` attribute in `@TestPropertySource` is set
|
|
|
|
|
to `false`, the locations or inlined properties, respectively, for the test class __shadow__
|
2015-03-03 18:38:01 +08:00
|
|
|
|
and effectively replace the configuration defined by superclasses.
|
|
|
|
|
|
|
|
|
|
In the following example, the `ApplicationContext` for `BaseTest` will be loaded using
|
|
|
|
|
only the `"base.properties"` file as a test property source. In contrast, the
|
|
|
|
|
`ApplicationContext` for `ExtendedTest` will be loaded using the `"base.properties"`
|
|
|
|
|
**and** `"extended.properties"` files as test property source locations.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@TestPropertySource("base.properties")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@ContextConfiguration
|
|
|
|
|
public class BaseTest {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@TestPropertySource("extended.properties")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@ContextConfiguration
|
|
|
|
|
public class ExtendedTest extends BaseTest {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
In the following example, the `ApplicationContext` for `BaseTest` will be loaded using only
|
|
|
|
|
the _inlined_ `key1` property. In contrast, the `ApplicationContext` for `ExtendedTest` will be
|
|
|
|
|
loaded using the _inlined_ `key1` and `key2` properties.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@TestPropertySource(properties = "key1 = value1")
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
public class BaseTest {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@TestPropertySource(properties = "key2 = value2")
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
public class ExtendedTest extends BaseTest {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-web]]
|
|
|
|
|
===== Loading a WebApplicationContext
|
|
|
|
|
Spring 3.2 introduced support for loading a `WebApplicationContext` in integration
|
|
|
|
|
tests. To instruct the TestContext framework to load a `WebApplicationContext` instead
|
|
|
|
|
of a standard `ApplicationContext`, simply annotate the respective test class with
|
|
|
|
|
`@WebAppConfiguration`.
|
|
|
|
|
|
|
|
|
|
The presence of `@WebAppConfiguration` on your test class instructs the TestContext
|
|
|
|
|
framework (TCF) that a `WebApplicationContext` (WAC) should be loaded for your
|
|
|
|
|
integration tests. In the background the TCF makes sure that a `MockServletContext` is
|
|
|
|
|
created and supplied to your test's WAC. By default the base resource path for your
|
|
|
|
|
`MockServletContext` will be set to __"src/main/webapp"__. 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 __"src/main/webapp"__ is the default location for the root of your WAR. If you
|
|
|
|
|
need to override this default, simply provide an alternate path to the
|
|
|
|
|
`@WebAppConfiguration` annotation (e.g., `@WebAppConfiguration("src/test/webapp")`). If
|
|
|
|
|
you wish to reference a base resource path from the classpath instead of the file
|
|
|
|
|
system, just use Spring's __classpath:__ prefix.
|
|
|
|
|
|
2015-07-26 00:22:26 +08:00
|
|
|
|
Please note that Spring's testing support for `WebApplicationContexts` is on par with its
|
|
|
|
|
support for standard `ApplicationContexts`. When testing with a `WebApplicationContext`
|
|
|
|
|
you are free to declare XML configuration files, Groovy scripts, or `@Configuration`
|
|
|
|
|
classes via `@ContextConfiguration`. You are of course also free to use any other test
|
|
|
|
|
annotations such as `@ActiveProfiles`, `@TestExecutionListeners`, `@Sql`, `@Rollback`,
|
|
|
|
|
etc.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
The following examples demonstrate some of the various configuration options for loading
|
|
|
|
|
a `WebApplicationContext`.
|
|
|
|
|
|
|
|
|
|
.Conventions
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
// defaults to "file:src/main/webapp"
|
|
|
|
|
@WebAppConfiguration
|
|
|
|
|
|
|
|
|
|
// detects "WacTests-context.xml" in same package
|
|
|
|
|
// or static nested @Configuration class
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
|
|
|
|
|
public class WacTests {
|
|
|
|
|
//...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
The above example demonstrates the TestContext framework's support for __convention over
|
|
|
|
|
configuration__. If you annotate a test class with `@WebAppConfiguration` without
|
|
|
|
|
specifying a resource base path, the resource path will effectively default
|
|
|
|
|
to __"file:src/main/webapp"__. Similarly, if you declare `@ContextConfiguration` without
|
|
|
|
|
specifying resource `locations`, annotated `classes`, or context `initializers`, Spring
|
|
|
|
|
will attempt to detect the presence of your configuration using conventions
|
|
|
|
|
(i.e., __"WacTests-context.xml"__ in the same package as the `WacTests` class or static
|
|
|
|
|
nested `@Configuration` classes).
|
|
|
|
|
|
|
|
|
|
.Default resource semantics
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
// file system resource
|
|
|
|
|
@WebAppConfiguration("webapp")
|
|
|
|
|
|
|
|
|
|
// classpath resource
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration("/spring/test-servlet-config.xml")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
public class WacTests {
|
|
|
|
|
//...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
This example demonstrates how to explicitly declare a resource base path with
|
|
|
|
|
`@WebAppConfiguration` and an XML resource location with `@ContextConfiguration`. The
|
|
|
|
|
important thing to note here is the different semantics for paths with these two
|
|
|
|
|
annotations. By default, `@WebAppConfiguration` resource paths are file system based;
|
|
|
|
|
whereas, `@ContextConfiguration` resource locations are classpath based.
|
|
|
|
|
|
|
|
|
|
.Explicit resource semantics
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
// classpath resource
|
|
|
|
|
@WebAppConfiguration("classpath:test-web-resources")
|
|
|
|
|
|
|
|
|
|
// file system resource
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
public class WacTests {
|
|
|
|
|
//...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
.[[testcontext-ctx-management-web-mocks]]Working with Web Mocks
|
|
|
|
|
--
|
|
|
|
|
To provide comprehensive web testing support, Spring 3.2 introduced a
|
|
|
|
|
`ServletTestExecutionListener` that is enabled by default. When testing against a
|
|
|
|
|
`WebApplicationContext` this <<testcontext-key-abstractions,TestExecutionListener>> sets
|
|
|
|
|
up default thread-local state via Spring Web's `RequestContextHolder` before each test
|
|
|
|
|
method and creates a `MockHttpServletRequest`, `MockHttpServletResponse`, and
|
|
|
|
|
`ServletWebRequest` based on the base resource path configured via
|
|
|
|
|
`@WebAppConfiguration`. `ServletTestExecutionListener` also ensures that the
|
|
|
|
|
`MockHttpServletResponse` and `ServletWebRequest` can be injected into the test
|
|
|
|
|
instance, and once the test is complete it cleans up thread-local state.
|
|
|
|
|
|
|
|
|
|
Once you have a `WebApplicationContext` 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
|
|
|
|
|
`WebApplicationContext` and `MockServletContext` are both cached across the test suite;
|
|
|
|
|
whereas, the other mocks are managed per test method by the
|
|
|
|
|
`ServletTestExecutionListener`.
|
|
|
|
|
|
|
|
|
|
.Injecting mocks
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@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;
|
|
|
|
|
|
|
|
|
|
//...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-caching]]
|
|
|
|
|
===== Context caching
|
|
|
|
|
|
|
|
|
|
Once the TestContext framework loads an `ApplicationContext` (or `WebApplicationContext`)
|
|
|
|
|
for a test, that context will be cached and reused for __all__ 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 meant by __unique__ and __test
|
|
|
|
|
suite__.
|
|
|
|
|
|
|
|
|
|
An `ApplicationContext` can be __uniquely__ identified by the combination of
|
2017-05-28 20:28:57 +08:00
|
|
|
|
configuration parameters that is used to load it. Consequently, the unique combination
|
|
|
|
|
of configuration parameters is used to generate a __key__ under which the context is
|
2015-03-03 18:38:01 +08:00
|
|
|
|
cached. The TestContext framework uses the following configuration parameters to build
|
|
|
|
|
the context cache key:
|
|
|
|
|
|
|
|
|
|
* `locations` __(from @ContextConfiguration)__
|
|
|
|
|
* `classes` __(from @ContextConfiguration)__
|
|
|
|
|
* `contextInitializerClasses` __(from @ContextConfiguration)__
|
2016-04-06 20:45:28 +08:00
|
|
|
|
* `contextCustomizers` __(from ContextCustomizerFactory)__
|
2015-03-03 18:38:01 +08:00
|
|
|
|
* `contextLoader` __(from @ContextConfiguration)__
|
|
|
|
|
* `parent` __(from @ContextHierarchy)__
|
|
|
|
|
* `activeProfiles` __(from @ActiveProfiles)__
|
|
|
|
|
* `propertySourceLocations` __(from @TestPropertySource)__
|
|
|
|
|
* `propertySourceProperties` __(from @TestPropertySource)__
|
|
|
|
|
* `resourceBasePath` __(from @WebAppConfiguration)__
|
|
|
|
|
|
|
|
|
|
For example, if `TestClassA` specifies `{"app-config.xml", "test-config.xml"}` for the
|
|
|
|
|
`locations` (or `value`) attribute of `@ContextConfiguration`, the TestContext framework
|
|
|
|
|
will load the corresponding `ApplicationContext` and store it in a `static` context cache
|
|
|
|
|
under a key that is based solely on those locations. So if `TestClassB` also defines
|
|
|
|
|
`{"app-config.xml", "test-config.xml"}` for its locations (either explicitly or
|
|
|
|
|
implicitly through inheritance) but does not define `@WebAppConfiguration`, a different
|
|
|
|
|
`ContextLoader`, different active profiles, different context initializers, different
|
|
|
|
|
test property sources, or a different parent context, then the same `ApplicationContext`
|
|
|
|
|
will be shared by both test classes. This means that the setup cost for loading an
|
|
|
|
|
application context is incurred only once (per test suite), and subsequent test execution
|
|
|
|
|
is much faster.
|
|
|
|
|
|
|
|
|
|
.Test suites and forked processes
|
|
|
|
|
[NOTE]
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
The Spring TestContext framework stores application contexts in a __static__ cache. This
|
|
|
|
|
means that the context is literally stored in a `static` variable. In other words, if
|
|
|
|
|
tests execute in separate processes the static cache will be cleared between each test
|
|
|
|
|
execution, and this will effectively disable the caching mechanism.
|
|
|
|
|
|
|
|
|
|
To benefit from the caching mechanism, all tests must run within the same process or
|
|
|
|
|
test suite. This can be achieved by executing all tests as a group within an IDE.
|
|
|
|
|
Similarly, when executing tests with a build framework such as Ant, Maven, or Gradle it
|
|
|
|
|
is important to make sure that the build framework does not __fork__ between tests. For
|
|
|
|
|
example, if the
|
|
|
|
|
http://maven.apache.org/plugins/maven-surefire-plugin/test-mojo.html#forkMode[forkMode]
|
|
|
|
|
for the Maven Surefire plug-in is set to `always` or `pertest`, the TestContext
|
|
|
|
|
framework will not be able to cache application contexts between test classes and the
|
|
|
|
|
build process will run significantly slower as a result.
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2016-04-06 21:06:10 +08:00
|
|
|
|
Since Spring Framework 4.3, the size of the context cache is bounded with a default
|
|
|
|
|
maximum size of 32. Whenever the maximum size is reached, a _least recently used_ (LRU)
|
|
|
|
|
eviction policy is used to evict and close stale contexts. The maximum size can be
|
|
|
|
|
configured from the command line or a build script by setting a JVM system property named
|
|
|
|
|
`spring.test.context.cache.maxSize`. As an alternative, the same property can be set
|
|
|
|
|
programmatically via the `SpringProperties` API.
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
Since having a large number of application contexts loaded within a given test suite can
|
|
|
|
|
cause the suite to take an unnecessarily long time to execute, it is often beneficial to
|
|
|
|
|
know exactly how many contexts have been loaded and cached. To view the statistics for
|
|
|
|
|
the underlying context cache, simply set the log level for the
|
|
|
|
|
`org.springframework.test.context.cache` logging category to `DEBUG`.
|
|
|
|
|
|
|
|
|
|
In the unlikely case that a test corrupts the application context and requires reloading
|
|
|
|
|
-- for example, by modifying a bean definition or the state of an application object --
|
|
|
|
|
you can annotate your test class or test method with `@DirtiesContext` (see the
|
|
|
|
|
discussion of `@DirtiesContext` in <<integration-testing-annotations-spring>>). This
|
|
|
|
|
instructs Spring to remove the context from the cache and rebuild the application
|
|
|
|
|
context before executing the next test. Note that support for the `@DirtiesContext`
|
2016-04-06 19:55:19 +08:00
|
|
|
|
annotation is provided by the `DirtiesContextBeforeModesTestExecutionListener` and the
|
|
|
|
|
`DirtiesContextTestExecutionListener` which are enabled by default.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-ctx-management-ctx-hierarchies]]
|
|
|
|
|
===== Context hierarchies
|
|
|
|
|
|
|
|
|
|
When writing integration tests that rely on a loaded Spring `ApplicationContext`, it is
|
|
|
|
|
often sufficient to test against a single context; however, there are times when it is
|
2016-06-11 18:51:38 +08:00
|
|
|
|
beneficial or even necessary to test against a hierarchy of ``ApplicationContext``s. For
|
2015-03-03 18:38:01 +08:00
|
|
|
|
example, if you are developing a Spring MVC web application you will typically have a
|
|
|
|
|
root `WebApplicationContext` loaded via Spring's `ContextLoaderListener` and a child
|
|
|
|
|
`WebApplicationContext` loaded via Spring's `DispatcherServlet`. This results in a
|
|
|
|
|
parent-child context hierarchy where shared components and infrastructure configuration
|
|
|
|
|
are declared in the root context and consumed in the child context by web-specific
|
|
|
|
|
components. Another use case can be found in Spring Batch applications where you often
|
|
|
|
|
have a parent context that provides configuration for shared batch infrastructure and a
|
|
|
|
|
child context for the configuration of a specific batch job.
|
|
|
|
|
|
2016-04-06 20:45:28 +08:00
|
|
|
|
Since Spring Framework 3.2.2, it is possible to write integration tests that use context
|
2015-03-03 18:38:01 +08:00
|
|
|
|
hierarchies by declaring context configuration via the `@ContextHierarchy` annotation,
|
|
|
|
|
either on an individual test class or within a test class hierarchy. If a context
|
|
|
|
|
hierarchy is declared on multiple classes within a test class hierarchy it is also
|
|
|
|
|
possible to merge or override the context configuration for a specific, named level in
|
|
|
|
|
the context hierarchy. When merging configuration for a given level in the hierarchy the
|
|
|
|
|
configuration resource type (i.e., XML configuration files or annotated classes) must be
|
|
|
|
|
consistent; otherwise, it is perfectly acceptable to have different levels in a context
|
|
|
|
|
hierarchy configured using different resource types.
|
|
|
|
|
|
2016-05-04 00:45:47 +08:00
|
|
|
|
The following JUnit 4 based examples demonstrate common configuration scenarios for
|
2015-03-03 18:38:01 +08:00
|
|
|
|
integration tests that require the use of context hierarchies.
|
|
|
|
|
|
|
|
|
|
.Single test class with context hierarchy
|
|
|
|
|
--
|
|
|
|
|
`ControllerIntegrationTests` represents a typical integration testing scenario for a
|
|
|
|
|
Spring MVC web application by declaring a context hierarchy consisting of two levels,
|
|
|
|
|
one for the __root__ WebApplicationContext (loaded using the `TestAppConfig`
|
|
|
|
|
`@Configuration` class) and one for the __dispatcher servlet__ `WebApplicationContext`
|
|
|
|
|
(loaded using the `WebConfig` `@Configuration` class). The `WebApplicationContext` that
|
|
|
|
|
is __autowired__ into the test instance is the one for the child context (i.e., the
|
|
|
|
|
lowest context in the hierarchy).
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@WebAppConfiguration
|
|
|
|
|
@ContextHierarchy({
|
|
|
|
|
@ContextConfiguration(classes = TestAppConfig.class),
|
|
|
|
|
@ContextConfiguration(classes = WebConfig.class)
|
|
|
|
|
})
|
|
|
|
|
public class ControllerIntegrationTests {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private WebApplicationContext wac;
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.Class hierarchy with implicit parent context
|
|
|
|
|
--
|
|
|
|
|
The following test classes define a context hierarchy within a test class hierarchy.
|
|
|
|
|
`AbstractWebTests` declares the configuration for a root `WebApplicationContext` in a
|
|
|
|
|
Spring-powered web application. Note, however, that `AbstractWebTests` does not declare
|
|
|
|
|
`@ContextHierarchy`; consequently, subclasses of `AbstractWebTests` can optionally
|
|
|
|
|
participate in a context hierarchy or simply follow the standard semantics for
|
|
|
|
|
`@ContextConfiguration`. `SoapWebServiceTests` and `RestWebServiceTests` both extend
|
|
|
|
|
`AbstractWebTests` and define a context hierarchy via `@ContextHierarchy`. The result is
|
|
|
|
|
that three application contexts will be loaded (one for each declaration of
|
|
|
|
|
`@ContextConfiguration`), and the application context loaded based on the configuration
|
|
|
|
|
in `AbstractWebTests` will be set as the parent context for each of the contexts loaded
|
|
|
|
|
for the concrete subclasses.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@WebAppConfiguration
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public abstract class AbstractWebTests {}
|
|
|
|
|
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class SoapWebServiceTests extends AbstractWebTests {}
|
|
|
|
|
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class RestWebServiceTests extends AbstractWebTests {}
|
|
|
|
|
----
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.Class hierarchy with merged context hierarchy configuration
|
|
|
|
|
--
|
|
|
|
|
The following classes demonstrate the use of __named__ hierarchy levels in order to
|
|
|
|
|
__merge__ the configuration for specific levels in a context hierarchy. `BaseTests`
|
|
|
|
|
defines two levels in the hierarchy, `parent` and `child`. `ExtendedTests` extends
|
|
|
|
|
`BaseTests` and instructs the Spring TestContext Framework to merge the context
|
|
|
|
|
configuration for the `child` hierarchy level, simply by ensuring that the names
|
2015-06-21 22:25:00 +08:00
|
|
|
|
declared via the `name` attribute in `@ContextConfiguration` are both `"child"`. The
|
|
|
|
|
result is that three application contexts will be loaded: one for `"/app-config.xml"`,
|
|
|
|
|
one for `"/user-config.xml"`, and one for `{"/user-config.xml", "/order-config.xml"}`.
|
|
|
|
|
As with the previous example, the application context loaded from `"/app-config.xml"`
|
|
|
|
|
will be set as the parent context for the contexts loaded from `"/user-config.xml"`
|
|
|
|
|
and `{"/user-config.xml", "/order-config.xml"}`.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@ContextHierarchy({
|
|
|
|
|
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
|
|
|
|
|
@ContextConfiguration(name = "child", locations = "/user-config.xml")
|
|
|
|
|
})
|
|
|
|
|
public class BaseTests {}
|
|
|
|
|
|
|
|
|
|
@ContextHierarchy(
|
|
|
|
|
@ContextConfiguration(name = "child", locations = "/order-config.xml")
|
|
|
|
|
)
|
|
|
|
|
public class ExtendedTests extends BaseTests {}
|
|
|
|
|
----
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
.Class hierarchy with overridden context hierarchy configuration
|
|
|
|
|
--
|
|
|
|
|
In contrast to the previous example, this example demonstrates how to __override__ the
|
2015-06-21 22:25:00 +08:00
|
|
|
|
configuration for a given named level in a context hierarchy by setting the
|
|
|
|
|
`inheritLocations` flag in `@ContextConfiguration` to `false`. Consequently, the
|
2015-03-03 18:38:01 +08:00
|
|
|
|
application context for `ExtendedTests` will be loaded only from
|
|
|
|
|
`"/test-user-config.xml"` and will have its parent set to the context loaded from
|
|
|
|
|
`"/app-config.xml"`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@ContextHierarchy({
|
|
|
|
|
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
|
|
|
|
|
@ContextConfiguration(name = "child", locations = "/user-config.xml")
|
|
|
|
|
})
|
|
|
|
|
public class BaseTests {}
|
|
|
|
|
|
|
|
|
|
@ContextHierarchy(
|
|
|
|
|
@ContextConfiguration(
|
|
|
|
|
name = "child",
|
|
|
|
|
locations = "/test-user-config.xml",
|
|
|
|
|
inheritLocations = false
|
|
|
|
|
))
|
|
|
|
|
public class ExtendedTests extends BaseTests {}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
.Dirtying a context within a context hierarchy
|
|
|
|
|
[NOTE]
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
If `@DirtiesContext` is used in a test whose context is configured as part of a context
|
|
|
|
|
hierarchy, the `hierarchyMode` flag can be used to control how the context cache is
|
|
|
|
|
cleared. For further details consult the discussion of `@DirtiesContext` in
|
|
|
|
|
<<integration-testing-annotations-spring,Spring Testing Annotations>> and the
|
|
|
|
|
`@DirtiesContext` javadocs.
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-fixture-di]]
|
|
|
|
|
==== Dependency injection of test fixtures
|
|
|
|
|
When you use the `DependencyInjectionTestExecutionListener` -- which is configured by
|
|
|
|
|
default -- the dependencies of your test instances are __injected__ from beans in the
|
|
|
|
|
application context that you configured with `@ContextConfiguration`. You may use setter
|
|
|
|
|
injection, field injection, or both, depending on which annotations you choose and
|
|
|
|
|
whether you place them on setter methods or fields. For consistency with the annotation
|
|
|
|
|
support introduced in Spring 2.5 and 3.0, you can use Spring's `@Autowired` annotation
|
2016-04-06 19:56:57 +08:00
|
|
|
|
or the `@Inject` annotation from JSR 330.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[TIP]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
The TestContext framework does not instrument the manner in which a test instance is
|
|
|
|
|
instantiated. Thus the use of `@Autowired` or `@Inject` for constructors has no effect
|
|
|
|
|
for test classes.
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-03-29 20:20:12 +08:00
|
|
|
|
Because `@Autowired` is used to perform <<core.adoc#beans-factory-autowire, __autowiring by type__
|
2015-03-03 18:38:01 +08:00
|
|
|
|
>>, if you have multiple bean definitions of the same type, you cannot rely on this
|
|
|
|
|
approach for those particular beans. In that case, you can use `@Autowired` in
|
|
|
|
|
conjunction with `@Qualifier`. As of Spring 3.0 you may also choose to use `@Inject` in
|
|
|
|
|
conjunction with `@Named`. Alternatively, if your test class has access to its
|
|
|
|
|
`ApplicationContext`, you can perform an explicit lookup by using (for example) a call
|
|
|
|
|
to `applicationContext.getBean("titleRepository")`.
|
|
|
|
|
|
|
|
|
|
If you do not want dependency injection applied to your test instances, simply do not
|
|
|
|
|
annotate fields or setter methods with `@Autowired` or `@Inject`. Alternatively, you can
|
|
|
|
|
disable dependency injection altogether by explicitly configuring your class with
|
|
|
|
|
`@TestExecutionListeners` and omitting `DependencyInjectionTestExecutionListener.class`
|
|
|
|
|
from the list of listeners.
|
|
|
|
|
|
|
|
|
|
Consider the scenario of testing a `HibernateTitleRepository` class, as outlined in the
|
|
|
|
|
<<integration-testing-goals,Goals>> section. The next two code listings demonstrate the
|
|
|
|
|
use of `@Autowired` on fields and setter methods. The application context configuration
|
|
|
|
|
is presented after all sample code listings.
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
The dependency injection behavior in the following code listings is not specific to
|
2016-05-04 00:45:47 +08:00
|
|
|
|
JUnit 4. The same DI techniques can be used in conjunction with any testing framework.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
The following examples make calls to static assertion methods such as `assertNotNull()`
|
|
|
|
|
but without prepending the call with `Assert`. In such cases, assume that the method was
|
|
|
|
|
properly imported through an `import static` declaration that is not shown in the
|
|
|
|
|
example.
|
2015-03-06 23:57:32 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2016-05-04 00:45:47 +08:00
|
|
|
|
The first code listing shows a JUnit 4 based implementation of the test class that uses
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`@Autowired` for field injection.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// specifies the Spring configuration to load for this test fixture
|
2016-08-26 19:57:18 +08:00
|
|
|
|
**@ContextConfiguration("repository-config.xml")**
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class HibernateTitleRepositoryTests {
|
|
|
|
|
|
|
|
|
|
// this instance will be dependency injected by type
|
|
|
|
|
**@Autowired**
|
|
|
|
|
private HibernateTitleRepository titleRepository;
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void findById() {
|
|
|
|
|
Title title = titleRepository.findById(new Long(10));
|
|
|
|
|
assertNotNull(title);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Alternatively, you can configure the class to use `@Autowired` for setter injection as
|
|
|
|
|
seen below.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// specifies the Spring configuration to load for this test fixture
|
2016-08-26 19:57:18 +08:00
|
|
|
|
**@ContextConfiguration("repository-config.xml")**
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class HibernateTitleRepositoryTests {
|
|
|
|
|
|
|
|
|
|
// this instance will be dependency injected by type
|
|
|
|
|
private HibernateTitleRepository titleRepository;
|
|
|
|
|
|
|
|
|
|
**@Autowired**
|
|
|
|
|
public void setTitleRepository(HibernateTitleRepository titleRepository) {
|
|
|
|
|
this.titleRepository = titleRepository;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void findById() {
|
|
|
|
|
Title title = titleRepository.findById(new Long(10));
|
|
|
|
|
assertNotNull(title);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
The preceding code listings use the same XML context file referenced by the
|
|
|
|
|
`@ContextConfiguration` annotation (that is, `repository-config.xml`), which looks like
|
|
|
|
|
this:
|
|
|
|
|
|
|
|
|
|
[source,xml,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
|
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
|
|
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
|
|
|
|
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
|
|
|
|
|
|
|
|
|
<!-- this bean will be injected into the HibernateTitleRepositoryTests class -->
|
|
|
|
|
<bean id="**titleRepository**" class="**com.foo.repository.hibernate.HibernateTitleRepository**">
|
|
|
|
|
<property name="sessionFactory" ref="sessionFactory"/>
|
|
|
|
|
</bean>
|
|
|
|
|
|
2015-12-18 07:27:19 +08:00
|
|
|
|
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
|
2015-03-03 18:38:01 +08:00
|
|
|
|
<!-- configuration elided for brevity -->
|
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
|
|
</beans>
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
If you are extending from a Spring-provided test base class that happens to use
|
|
|
|
|
`@Autowired` on one of its setter methods, you might have multiple beans of the affected
|
|
|
|
|
type defined in your application context: for example, multiple `DataSource` beans. In
|
|
|
|
|
such a case, you can override the setter method and use the `@Qualifier` annotation to
|
|
|
|
|
indicate a specific target bean as follows, but make sure to delegate to the overridden
|
|
|
|
|
method in the superclass as well.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
@Override
|
|
|
|
|
public void setDataSource(**@Qualifier("myDataSource")** DataSource dataSource) {
|
|
|
|
|
**super**.setDataSource(dataSource);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
The specified qualifier value indicates the specific `DataSource` bean to inject,
|
|
|
|
|
narrowing the set of type matches to a specific bean. Its value is matched against
|
|
|
|
|
`<qualifier>` declarations within the corresponding `<bean>` definitions. The bean name
|
|
|
|
|
is used as a fallback qualifier value, so you may effectively also point to a specific
|
|
|
|
|
bean by name there (as shown above, assuming that "myDataSource" is the bean id).
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-web-scoped-beans]]
|
|
|
|
|
==== Testing request and session scoped beans
|
|
|
|
|
|
|
|
|
|
<<beans-factory-scopes-other,Request and session scoped beans>> have been supported by
|
2016-04-06 20:45:28 +08:00
|
|
|
|
Spring since the early years, and since Spring 3.2 it's a breeze to test your
|
|
|
|
|
request-scoped and session-scoped beans by following these steps.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
* Ensure that a `WebApplicationContext` is loaded for your test by annotating your test
|
|
|
|
|
class with `@WebAppConfiguration`.
|
|
|
|
|
* Inject the mock request or session into your test instance and prepare your test
|
|
|
|
|
fixture as appropriate.
|
|
|
|
|
* Invoke your web component that you retrieved from the configured
|
|
|
|
|
`WebApplicationContext` (i.e., via dependency injection).
|
|
|
|
|
* Perform assertions against the mocks.
|
|
|
|
|
|
|
|
|
|
The following code snippet displays the XML configuration for a login use case. Note
|
|
|
|
|
that the `userService` bean has a dependency on a request-scoped `loginAction` bean.
|
2017-03-29 20:20:12 +08:00
|
|
|
|
Also, the `LoginAction` is instantiated using <<core.adoc#expressions,SpEL expressions>> that
|
2015-03-03 18:38:01 +08:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
.Request-scoped bean configuration
|
|
|
|
|
[source,xml,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
<beans>
|
|
|
|
|
|
|
|
|
|
<bean id="userService"
|
|
|
|
|
class="com.example.SimpleUserService"
|
|
|
|
|
c:loginAction-ref="loginAction" />
|
|
|
|
|
|
|
|
|
|
<bean id="loginAction" class="com.example.LoginAction"
|
2017-09-28 22:44:08 +08:00
|
|
|
|
c:username="\#{request.getParameter('user')}"
|
2016-04-06 20:14:51 +08:00
|
|
|
|
c:password="#{request.getParameter('pswd')}"
|
2015-03-03 18:38:01 +08:00
|
|
|
|
scope="request">
|
|
|
|
|
<aop:scoped-proxy />
|
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
|
|
</beans>
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
In `RequestScopedBeanTests` we inject both the `UserService` (i.e., the subject under
|
|
|
|
|
test) and the `MockHttpServletRequest` into our test instance. Within our
|
|
|
|
|
`requestScope()` test method we set up our test fixture by setting request parameters in
|
|
|
|
|
the provided `MockHttpServletRequest`. When the `loginUser()` method is invoked on our
|
|
|
|
|
`userService` we are assured that the user service has access to the request-scoped
|
|
|
|
|
`loginAction` for the current `MockHttpServletRequest` (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.
|
|
|
|
|
|
|
|
|
|
.Request-scoped bean test
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
The following code snippet is similar to the one we saw above for a request-scoped bean;
|
|
|
|
|
however, this time the `userService` bean has a dependency on a session-scoped
|
|
|
|
|
`userPreferences` bean. Note that the `UserPreferences` bean is instantiated using a
|
|
|
|
|
SpEL expression that retrieves the __theme__ from the current HTTP session. In our test,
|
|
|
|
|
we will need to configure a theme in the mock session managed by the TestContext
|
|
|
|
|
framework.
|
|
|
|
|
|
|
|
|
|
.Session-scoped bean configuration
|
|
|
|
|
[source,xml,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
<beans>
|
|
|
|
|
|
|
|
|
|
<bean id="userService"
|
|
|
|
|
class="com.example.SimpleUserService"
|
|
|
|
|
c:userPreferences-ref="userPreferences" />
|
|
|
|
|
|
|
|
|
|
<bean id="userPreferences"
|
|
|
|
|
class="com.example.UserPreferences"
|
2016-04-06 20:14:51 +08:00
|
|
|
|
c:theme="#{session.getAttribute('theme')}"
|
2015-03-03 18:38:01 +08:00
|
|
|
|
scope="session">
|
|
|
|
|
<aop:scoped-proxy />
|
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
|
|
</beans>
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
In `SessionScopedBeanTests` we inject the `UserService` and the `MockHttpSession` into
|
|
|
|
|
our test instance. Within our `sessionScope()` test method we set up our test fixture by
|
|
|
|
|
setting the expected "theme" attribute in the provided `MockHttpSession`. When the
|
|
|
|
|
`processUserPreferences()` method is invoked on our `userService` we are assured that
|
|
|
|
|
the user service has access to the session-scoped `userPreferences` for the current
|
|
|
|
|
`MockHttpSession`, and we can perform assertions against the results based on the
|
|
|
|
|
configured theme.
|
|
|
|
|
|
|
|
|
|
.Session-scoped bean test
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-tx]]
|
|
|
|
|
==== Transaction management
|
|
|
|
|
|
|
|
|
|
In the TestContext framework, transactions are managed by the
|
|
|
|
|
`TransactionalTestExecutionListener` which is configured by default, even if you do not
|
|
|
|
|
explicitly declare `@TestExecutionListeners` on your test class. To enable support for
|
|
|
|
|
transactions, however, you must configure a `PlatformTransactionManager` bean in the
|
|
|
|
|
`ApplicationContext` that is loaded via `@ContextConfiguration` semantics (further
|
|
|
|
|
details are provided below). In addition, you must declare Spring's `@Transactional`
|
|
|
|
|
annotation either at the class or method level for your tests.
|
|
|
|
|
|
|
|
|
|
[[testcontext-tx-test-managed-transactions]]
|
|
|
|
|
===== Test-managed transactions
|
|
|
|
|
|
|
|
|
|
_Test-managed transactions_ are transactions that are managed _declaratively_ via the
|
|
|
|
|
`TransactionalTestExecutionListener` or _programmatically_ via `TestTransaction` (see
|
|
|
|
|
below). Such transactions should not be confused with _Spring-managed transactions_
|
|
|
|
|
(i.e., those managed directly by Spring within the `ApplicationContext` loaded for tests)
|
|
|
|
|
or _application-managed transactions_ (i.e., those managed programmatically within
|
|
|
|
|
application code that is invoked via tests). Spring-managed and application-managed
|
|
|
|
|
transactions will typically participate in test-managed transactions; however, caution
|
|
|
|
|
should be taken if Spring-managed or application-managed transactions are configured with
|
|
|
|
|
any _propagation_ type other than `REQUIRED` or `SUPPORTS` (see the discussion on
|
2017-03-29 20:20:12 +08:00
|
|
|
|
<<data-access.adoc#tx-propagation,transaction propagation>> for details).
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[testcontext-tx-enabling-transactions]]
|
|
|
|
|
===== Enabling and disabling transactions
|
|
|
|
|
|
|
|
|
|
Annotating a test method with `@Transactional` causes the test to be run within a
|
|
|
|
|
transaction that will, by default, be automatically rolled back after completion of the
|
|
|
|
|
test. If a test class is annotated with `@Transactional`, each test method within that
|
|
|
|
|
class hierarchy will be run within a transaction. Test methods that are not annotated
|
|
|
|
|
with `@Transactional` (at the class or method level) will not be run within a
|
|
|
|
|
transaction. Furthermore, tests that are annotated with `@Transactional` but have the
|
|
|
|
|
`propagation` type set to `NOT_SUPPORTED` will not be run within a transaction.
|
|
|
|
|
|
|
|
|
|
__Note that <<testcontext-support-classes-junit4,
|
|
|
|
|
`AbstractTransactionalJUnit4SpringContextTests`>> and
|
|
|
|
|
<<testcontext-support-classes-testng, `AbstractTransactionalTestNGSpringContextTests`>>
|
|
|
|
|
are preconfigured for transactional support at the class level.__
|
|
|
|
|
|
|
|
|
|
The following example demonstrates a common scenario for writing an integration test for
|
|
|
|
|
a Hibernate-based `UserRepository`. As explained in
|
|
|
|
|
<<testcontext-tx-rollback-and-commit-behavior>>, there is no need to clean up the
|
|
|
|
|
database after the `createUser()` method is executed since any changes made to the
|
|
|
|
|
database will be automatically rolled back by the `TransactionalTestExecutionListener`.
|
|
|
|
|
See <<testing-examples-petclinic>> for an additional example.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@ContextConfiguration(classes = TestConfig.class)
|
|
|
|
|
@Transactional
|
|
|
|
|
public class HibernateUserRepositoryTests {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
HibernateUserRepository repository;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
SessionFactory sessionFactory;
|
|
|
|
|
|
|
|
|
|
JdbcTemplate jdbcTemplate;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
public void setDataSource(DataSource dataSource) {
|
|
|
|
|
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void createUser() {
|
|
|
|
|
// track initial state in test database:
|
|
|
|
|
final int count = countRowsInTable("user");
|
|
|
|
|
|
|
|
|
|
User user = new User(...);
|
|
|
|
|
repository.save(user);
|
|
|
|
|
|
|
|
|
|
// Manual flush is required to avoid false positive in test
|
|
|
|
|
sessionFactory.getCurrentSession().flush();
|
|
|
|
|
assertNumUsers(count + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected int countRowsInTable(String tableName) {
|
|
|
|
|
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void assertNumUsers(int expected) {
|
2015-06-21 22:25:00 +08:00
|
|
|
|
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
|
2015-03-03 18:38:01 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-tx-rollback-and-commit-behavior]]
|
|
|
|
|
===== Transaction rollback and commit behavior
|
|
|
|
|
|
|
|
|
|
By default, test transactions will be automatically rolled back after completion of the
|
|
|
|
|
test; however, transactional commit and rollback behavior can be configured declaratively
|
2015-09-11 01:35:17 +08:00
|
|
|
|
via the `@Commit` and `@Rollback` annotations. See the corresponding entries in the
|
2015-07-26 00:22:26 +08:00
|
|
|
|
<<integration-testing-annotations,annotation support>> section for further details.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[testcontext-tx-programmatic-tx-mgt]]
|
|
|
|
|
===== Programmatic transaction management
|
2016-04-06 20:45:28 +08:00
|
|
|
|
Since Spring Framework 4.1, it is possible to interact with test-managed transactions
|
2015-03-03 18:38:01 +08:00
|
|
|
|
_programmatically_ via the static methods in `TestTransaction`. For example,
|
|
|
|
|
`TestTransaction` may be used within _test_ methods, _before_ methods, and _after_
|
|
|
|
|
methods to start or end the current test-managed transaction or to configure the current
|
|
|
|
|
test-managed transaction for rollback or commit. Support for `TestTransaction` is
|
|
|
|
|
automatically available whenever the `TransactionalTestExecutionListener` is enabled.
|
|
|
|
|
|
|
|
|
|
The following example demonstrates some of the features of `TestTransaction`. Consult the
|
|
|
|
|
javadocs for `TestTransaction` for further details.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration(classes = TestConfig.class)
|
|
|
|
|
public class ProgrammaticTransactionManagementTests extends
|
|
|
|
|
AbstractTransactionalJUnit4SpringContextTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void transactionalTest() {
|
|
|
|
|
// assert initial state in test database:
|
|
|
|
|
assertNumUsers(2);
|
|
|
|
|
|
|
|
|
|
deleteFromTables("user");
|
|
|
|
|
|
|
|
|
|
// changes to the database will be committed!
|
|
|
|
|
TestTransaction.flagForCommit();
|
|
|
|
|
TestTransaction.end();
|
|
|
|
|
assertFalse(TestTransaction.isActive());
|
|
|
|
|
assertNumUsers(0);
|
|
|
|
|
|
|
|
|
|
TestTransaction.start();
|
|
|
|
|
// perform other actions against the database that will
|
|
|
|
|
// be automatically rolled back after the test completes...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void assertNumUsers(int expected) {
|
2015-06-21 22:25:00 +08:00
|
|
|
|
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
|
2015-03-03 18:38:01 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-tx-before-and-after-tx]]
|
|
|
|
|
===== Executing code outside of a transaction
|
|
|
|
|
|
|
|
|
|
Occasionally you need to execute certain code before or after a transactional test method
|
|
|
|
|
but outside the transactional context -- for example, to verify the initial database state
|
|
|
|
|
prior to execution of your test or to verify expected transactional commit behavior after
|
2016-05-04 00:53:17 +08:00
|
|
|
|
test execution (if the test was configured to commit the transaction).
|
2015-03-03 18:38:01 +08:00
|
|
|
|
`TransactionalTestExecutionListener` supports the `@BeforeTransaction` and
|
2016-02-29 06:36:21 +08:00
|
|
|
|
`@AfterTransaction` annotations exactly for such scenarios. Simply annotate any `void`
|
2016-05-04 00:53:17 +08:00
|
|
|
|
method in a test class or any `void` default method in a test interface with one of these
|
|
|
|
|
annotations, and the `TransactionalTestExecutionListener` ensures that your __before
|
|
|
|
|
transaction method__ or __after transaction method__ is executed at the appropriate time.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[TIP]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2017-09-27 22:25:16 +08:00
|
|
|
|
Any __before methods__ (such as methods annotated with JUnit Jupiter's `@BeforeEach`) and
|
|
|
|
|
any __after methods__ (such as methods annotated with JUnit Jupiter's `@AfterEach`) are
|
|
|
|
|
executed __within__ a transaction. In addition, methods annotated with
|
|
|
|
|
`@BeforeTransaction` or `@AfterTransaction` are naturally not executed for test methods
|
|
|
|
|
that are not configured to run within a transaction.
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[testcontext-tx-mgr-config]]
|
|
|
|
|
===== Configuring a transaction manager
|
|
|
|
|
|
|
|
|
|
`TransactionalTestExecutionListener` expects a `PlatformTransactionManager` bean to be
|
|
|
|
|
defined in the Spring `ApplicationContext` for the test. In case there are multiple
|
2015-07-26 00:22:26 +08:00
|
|
|
|
instances of `PlatformTransactionManager` within the test's `ApplicationContext`, a
|
2016-08-26 19:57:18 +08:00
|
|
|
|
_qualifier_ may be declared via `@Transactional("myTxMgr")` or
|
2015-07-26 00:22:26 +08:00
|
|
|
|
`@Transactional(transactionManager = "myTxMgr")`, or `TransactionManagementConfigurer`
|
|
|
|
|
can be implemented by an `@Configuration` class. Consult the javadocs for
|
|
|
|
|
`TestContextTransactionUtils.retrieveTransactionManager()` for details on the algorithm
|
|
|
|
|
used to look up a transaction manager in the test's `ApplicationContext`.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[testcontext-tx-annotation-demo]]
|
|
|
|
|
===== Demonstration of all transaction-related annotations
|
|
|
|
|
|
2017-09-27 22:25:16 +08:00
|
|
|
|
The following JUnit 4 based example displays a _fictitious_ integration testing scenario
|
2015-03-03 18:38:01 +08:00
|
|
|
|
highlighting all transaction-related annotations. The example is **not** intended to
|
|
|
|
|
demonstrate best practices but rather to demonstrate how these annotations can be used.
|
|
|
|
|
Consult the <<integration-testing-annotations,annotation support>> section for further
|
|
|
|
|
information and configuration examples. <<testcontext-executing-sql-declaratively-tx,
|
|
|
|
|
Transaction management for `@Sql`>> contains an additional example using `@Sql` for
|
|
|
|
|
declarative SQL script execution with default transaction rollback semantics.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@ContextConfiguration
|
2015-10-16 06:48:04 +08:00
|
|
|
|
**@Transactional(transactionManager = "txMgr")**
|
2015-09-11 01:35:17 +08:00
|
|
|
|
**@Commit**
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class FictitiousTransactionalTest {
|
|
|
|
|
|
|
|
|
|
**@BeforeTransaction**
|
2016-02-29 06:36:21 +08:00
|
|
|
|
void verifyInitialDatabaseState() {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// logic to verify the initial state before a transaction is started
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setUpTestDataWithinTransaction() {
|
|
|
|
|
// set up test data within the transaction
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
2015-10-16 22:08:42 +08:00
|
|
|
|
// overrides the class-level @Commit setting
|
2015-09-11 01:35:17 +08:00
|
|
|
|
**@Rollback**
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public void modifyDatabaseWithinTransaction() {
|
|
|
|
|
// logic which uses the test data and modifies database state
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@After
|
|
|
|
|
public void tearDownWithinTransaction() {
|
|
|
|
|
// execute "tear down" logic within the transaction
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
**@AfterTransaction**
|
2016-02-29 06:36:21 +08:00
|
|
|
|
void verifyFinalDatabaseState() {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// logic to verify the final state after transaction has rolled back
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-tx-false-positives]]
|
|
|
|
|
.Avoid false positives when testing ORM code
|
|
|
|
|
[NOTE]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2016-05-04 01:33:32 +08:00
|
|
|
|
When you test application code that manipulates the state of a Hibernate session or JPA
|
|
|
|
|
persistence context, make sure to __flush__ the underlying unit of work within test
|
|
|
|
|
methods that execute that code. Failing to flush the underlying unit of work can produce
|
|
|
|
|
__false positives__: your test may pass, but the same code throws an exception in a live,
|
|
|
|
|
production environment. In the following Hibernate-based example test case, one method
|
|
|
|
|
demonstrates a false positive, and the other method correctly exposes the results of
|
|
|
|
|
flushing the session. Note that this applies to any ORM frameworks that maintain an
|
|
|
|
|
in-memory __unit of work__.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
@Autowired
|
2016-05-04 01:33:32 +08:00
|
|
|
|
SessionFactory sessionFactory;
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2016-05-04 01:33:32 +08:00
|
|
|
|
@Transactional
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@Test // no expected exception!
|
|
|
|
|
public void falsePositive() {
|
|
|
|
|
updateEntityInHibernateSession();
|
2015-11-25 23:58:17 +08:00
|
|
|
|
// False positive: an exception will be thrown once the Hibernate
|
|
|
|
|
// Session is finally flushed (i.e., in production code)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-04 01:33:32 +08:00
|
|
|
|
@Transactional
|
2015-11-25 23:58:17 +08:00
|
|
|
|
@Test(expected = ...)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public void updateWithSessionFlush() {
|
|
|
|
|
updateEntityInHibernateSession();
|
|
|
|
|
// Manual flush is required to avoid false positive in test
|
|
|
|
|
sessionFactory.getCurrentSession().flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
----
|
2015-11-25 23:58:17 +08:00
|
|
|
|
|
|
|
|
|
Or for JPA:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
// ...
|
|
|
|
|
|
2016-05-04 01:33:32 +08:00
|
|
|
|
@PersistenceContext
|
|
|
|
|
EntityManager entityManager;
|
2015-11-25 23:58:17 +08:00
|
|
|
|
|
2016-05-04 01:33:32 +08:00
|
|
|
|
@Transactional
|
2015-11-25 23:58:17 +08:00
|
|
|
|
@Test // no expected exception!
|
|
|
|
|
public void falsePositive() {
|
2016-05-04 01:33:32 +08:00
|
|
|
|
updateEntityInJpaPersistenceContext();
|
2015-11-25 23:58:17 +08:00
|
|
|
|
// False positive: an exception will be thrown once the JPA
|
|
|
|
|
// EntityManager is finally flushed (i.e., in production code)
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-04 01:33:32 +08:00
|
|
|
|
@Transactional
|
2015-11-25 23:58:17 +08:00
|
|
|
|
@Test(expected = ...)
|
|
|
|
|
public void updateWithEntityManagerFlush() {
|
2016-05-04 01:33:32 +08:00
|
|
|
|
updateEntityInJpaPersistenceContext();
|
2015-11-25 23:58:17 +08:00
|
|
|
|
// Manual flush is required to avoid false positive in test
|
|
|
|
|
entityManager.flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
----
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-executing-sql]]
|
|
|
|
|
==== Executing SQL scripts
|
|
|
|
|
|
|
|
|
|
When writing integration tests against a relational database, it is often beneficial
|
|
|
|
|
to execute SQL scripts to modify the database schema or insert test data into tables.
|
|
|
|
|
The `spring-jdbc` module provides support for _initializing_ an embedded or existing
|
|
|
|
|
database by executing SQL scripts when the Spring `ApplicationContext` is loaded. See
|
2017-03-29 20:20:12 +08:00
|
|
|
|
<<data-access.adoc#jdbc-embedded-database-support, Embedded database support>> and
|
|
|
|
|
<<data-access.adoc#jdbc-embedded-database-dao-testing,
|
|
|
|
|
Testing data access logic with an embedded database>> for details.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
Although it is very useful to initialize a database for testing _once_ when the
|
|
|
|
|
`ApplicationContext` is loaded, sometimes it is essential to be able to modify the
|
|
|
|
|
database _during_ integration tests. The following sections explain how to execute SQL
|
|
|
|
|
scripts programmatically and declaratively during integration tests.
|
|
|
|
|
|
|
|
|
|
[[testcontext-executing-sql-programmatically]]
|
|
|
|
|
===== Executing SQL scripts programmatically
|
|
|
|
|
|
|
|
|
|
Spring provides the following options for executing SQL scripts programmatically within
|
|
|
|
|
integration test methods.
|
|
|
|
|
|
|
|
|
|
* `org.springframework.jdbc.datasource.init.ScriptUtils`
|
|
|
|
|
* `org.springframework.jdbc.datasource.init.ResourceDatabasePopulator`
|
|
|
|
|
* `org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests`
|
|
|
|
|
* `org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests`
|
|
|
|
|
|
|
|
|
|
`ScriptUtils` provides a collection of static utility methods for working with SQL scripts
|
|
|
|
|
and is mainly intended for internal use within the framework. However, if you require
|
|
|
|
|
full control over how SQL scripts are parsed and executed, `ScriptUtils` may suit your
|
|
|
|
|
needs better than some of the other alternatives described below. Consult the javadocs for
|
|
|
|
|
individual methods in `ScriptUtils` for further details.
|
|
|
|
|
|
|
|
|
|
`ResourceDatabasePopulator` provides a simple object-based API for programmatically
|
|
|
|
|
populating, initializing, or cleaning up a database using SQL scripts defined in
|
|
|
|
|
external resources. `ResourceDatabasePopulator` provides options for configuring the
|
|
|
|
|
character encoding, statement separator, comment delimiters, and error handling flags
|
|
|
|
|
used when parsing and executing the scripts, and each of the configuration options has
|
|
|
|
|
a reasonable default value. Consult the javadocs for details on default values. To
|
|
|
|
|
execute the scripts configured in a `ResourceDatabasePopulator`, you can invoke either
|
|
|
|
|
the `populate(Connection)` method to execute the populator against a
|
|
|
|
|
`java.sql.Connection` or the `execute(DataSource)` method to execute the populator
|
|
|
|
|
against a `javax.sql.DataSource`. The following example specifies SQL scripts for a test
|
|
|
|
|
schema and test data, sets the statement separator to `"@@"`, and then executes the
|
|
|
|
|
scripts against a `DataSource`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
public void databaseTest {
|
|
|
|
|
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
|
|
|
|
populator.addScripts(
|
|
|
|
|
new ClassPathResource("test-schema.sql"),
|
|
|
|
|
new ClassPathResource("test-data.sql"));
|
|
|
|
|
populator.setSeparator("@@");
|
|
|
|
|
populator.execute(this.dataSource);
|
|
|
|
|
// execute code that uses the test schema and data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Note that `ResourceDatabasePopulator` internally delegates to `ScriptUtils` for parsing
|
|
|
|
|
and executing SQL scripts. Similarly, the `executeSqlScript(..)` methods in
|
|
|
|
|
<<testcontext-support-classes-junit4, `AbstractTransactionalJUnit4SpringContextTests`>> and
|
|
|
|
|
<<testcontext-support-classes-testng, `AbstractTransactionalTestNGSpringContextTests`>>
|
|
|
|
|
internally use a `ResourceDatabasePopulator` for executing SQL scripts. Consult the javadocs
|
|
|
|
|
for the various `executeSqlScript(..)` methods for further details.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-executing-sql-declaratively]]
|
2016-05-04 22:04:37 +08:00
|
|
|
|
===== Executing SQL scripts declaratively with @Sql
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
In addition to the aforementioned mechanisms for executing SQL scripts
|
|
|
|
|
_programmatically_, SQL scripts can also be configured _declaratively_ in the Spring
|
|
|
|
|
TestContext Framework. Specifically, the `@Sql` annotation can be declared on a test
|
|
|
|
|
class or test method to configure the resource paths to SQL scripts that should be
|
|
|
|
|
executed against a given database either before or after an integration test method. Note
|
|
|
|
|
that method-level declarations override class-level declarations and that support for
|
|
|
|
|
`@Sql` is provided by the `SqlScriptsTestExecutionListener` which is enabled by default.
|
|
|
|
|
|
|
|
|
|
*Path resource semantics*
|
|
|
|
|
|
|
|
|
|
Each path will be interpreted as a Spring `Resource`. A plain path -- for example,
|
|
|
|
|
`"schema.sql"` -- will be treated as a classpath resource that is _relative_ to the
|
|
|
|
|
package in which the test class is defined. A path starting with a slash will be treated
|
|
|
|
|
as an _absolute_ classpath resource, for example: `"/org/example/schema.sql"`. A path
|
|
|
|
|
which references a URL (e.g., a path prefixed with `classpath:`, `file:`, `http:`, etc.)
|
|
|
|
|
will be loaded using the specified resource protocol.
|
|
|
|
|
|
|
|
|
|
The following example demonstrates how to use `@Sql` at the class level and at the method
|
2017-09-28 05:27:44 +08:00
|
|
|
|
level within a JUnit Jupiter based integration test class.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2017-09-28 05:27:44 +08:00
|
|
|
|
@SpringJUnitConfig
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@Sql("/test-schema.sql")
|
2017-09-28 05:27:44 +08:00
|
|
|
|
class DatabaseTests {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
@Test
|
2017-09-28 05:27:44 +08:00
|
|
|
|
void emptySchemaTest {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// execute code that uses the test schema without any test data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@Sql({"/test-schema.sql", "/test-user-data.sql"})
|
2017-09-28 05:27:44 +08:00
|
|
|
|
void userTest {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// execute code that uses the test schema and test data
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
*Default script detection*
|
|
|
|
|
|
|
|
|
|
If no SQL scripts are specified, an attempt will be made to detect a `default` script
|
|
|
|
|
depending on where `@Sql` is declared. If a default cannot be detected, an
|
|
|
|
|
`IllegalStateException` will be thrown.
|
|
|
|
|
|
|
|
|
|
* __class-level declaration__: if the annotated test class is `com.example.MyTest`, the
|
|
|
|
|
corresponding default script is `"classpath:com/example/MyTest.sql"`.
|
|
|
|
|
* __method-level declaration__: if the annotated test method is named `testMethod()` and is
|
|
|
|
|
defined in the class `com.example.MyTest`, the corresponding default script is
|
|
|
|
|
`"classpath:com/example/MyTest.testMethod.sql"`.
|
|
|
|
|
|
|
|
|
|
*Declaring multiple `@Sql` sets*
|
|
|
|
|
|
|
|
|
|
If multiple sets of SQL scripts need to be configured for a given test class or test
|
|
|
|
|
method but with different syntax configuration, different error handling rules, or
|
|
|
|
|
different execution phases per set, it is possible to declare multiple instances of
|
|
|
|
|
`@Sql`. With Java 8, `@Sql` can be used as a _repeatable_ annotation. Otherwise, the
|
|
|
|
|
`@SqlGroup` annotation can be used as an explicit container for declaring multiple
|
|
|
|
|
instances of `@Sql`.
|
|
|
|
|
|
|
|
|
|
The following example demonstrates the use of `@Sql` as a repeatable annotation using
|
|
|
|
|
Java 8. In this scenario the `test-schema.sql` script uses a different syntax for
|
|
|
|
|
single-line comments.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`"))
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@Sql("/test-user-data.sql")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public void userTest {
|
|
|
|
|
// execute code that uses the test schema and test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
The following example is identical to the above except that the `@Sql` declarations are
|
|
|
|
|
grouped together within `@SqlGroup` for compatibility with Java 6 and Java 7.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@SqlGroup({
|
|
|
|
|
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@Sql("/test-user-data.sql")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
)}
|
|
|
|
|
public void userTest {
|
|
|
|
|
// execute code that uses the test schema and test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
*Script execution phases*
|
|
|
|
|
|
|
|
|
|
By default, SQL scripts will be executed _before_ the corresponding test method. However,
|
|
|
|
|
if a particular set of scripts needs to be executed _after_ the test method -- for
|
|
|
|
|
example, to clean up database state -- the `executionPhase` attribute in `@Sql` can be
|
|
|
|
|
used as seen in the following example. Note that `ISOLATED` and `AFTER_TEST_METHOD` are
|
|
|
|
|
statically imported from `Sql.TransactionMode` and `Sql.ExecutionPhase` respectively.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@Sql(
|
|
|
|
|
scripts = "create-test-data.sql",
|
|
|
|
|
config = @SqlConfig(transactionMode = ISOLATED)
|
|
|
|
|
)
|
|
|
|
|
@Sql(
|
|
|
|
|
scripts = "delete-test-data.sql",
|
|
|
|
|
config = @SqlConfig(transactionMode = ISOLATED),
|
|
|
|
|
executionPhase = AFTER_TEST_METHOD
|
|
|
|
|
)
|
|
|
|
|
public void userTest {
|
|
|
|
|
// execute code that needs the test data to be committed
|
|
|
|
|
// to the database outside of the test's transaction
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
*Script configuration with `@SqlConfig`*
|
|
|
|
|
|
|
|
|
|
Configuration for script parsing and error handling can be configured via the
|
|
|
|
|
`@SqlConfig` annotation. When declared as a class-level annotation on an integration test
|
|
|
|
|
class, `@SqlConfig` serves as _global_ configuration for all SQL scripts within the test
|
|
|
|
|
class hierarchy. When declared directly via the `config` attribute of the `@Sql`
|
|
|
|
|
annotation, `@SqlConfig` serves as _local_ configuration for the SQL scripts declared
|
|
|
|
|
within the enclosing `@Sql` annotation. Every attribute in `@SqlConfig` has an implicit
|
|
|
|
|
default value which is documented in the javadocs of the corresponding attribute. Due to
|
|
|
|
|
the rules defined for annotation attributes in the Java Language Specification, it is
|
|
|
|
|
unfortunately not possible to assign a value of `null` to an annotation attribute. Thus,
|
|
|
|
|
in order to support overrides of inherited global configuration, `@SqlConfig` attributes
|
|
|
|
|
have an explicit default value of either `""` for Strings or `DEFAULT` for Enums. This
|
|
|
|
|
approach allows local declarations of `@SqlConfig` to selectively override individual
|
|
|
|
|
attributes from global declarations of `@SqlConfig` by providing a value other than `""`
|
|
|
|
|
or `DEFAULT`. Global `@SqlConfig` attributes are inherited whenever local `@SqlConfig`
|
|
|
|
|
attributes do not supply an explicit value other than `""` or `DEFAULT`. Explicit _local_
|
|
|
|
|
configuration therefore overrides _global_ configuration.
|
|
|
|
|
|
|
|
|
|
The configuration options provided by `@Sql` and `@SqlConfig` are equivalent to those
|
|
|
|
|
supported by `ScriptUtils` and `ResourceDatabasePopulator` but are a superset of those
|
|
|
|
|
provided by the `<jdbc:initialize-database/>` XML namespace element. Consult the javadocs
|
|
|
|
|
of individual attributes in `@Sql` and `@SqlConfig` for details.
|
|
|
|
|
|
|
|
|
|
[[testcontext-executing-sql-declaratively-tx]]
|
|
|
|
|
*Transaction management for `@Sql`*
|
|
|
|
|
|
|
|
|
|
By default, the `SqlScriptsTestExecutionListener` will infer the desired transaction
|
|
|
|
|
semantics for scripts configured via `@Sql`. Specifically, SQL scripts will be executed
|
|
|
|
|
without a transaction, within an existing Spring-managed transaction -- for example, a
|
|
|
|
|
transaction managed by the `TransactionalTestExecutionListener` for a test annotated with
|
|
|
|
|
`@Transactional` -- or within an isolated transaction, depending on the configured value
|
|
|
|
|
of the `transactionMode` attribute in `@SqlConfig` and the presence of a
|
|
|
|
|
`PlatformTransactionManager` in the test's `ApplicationContext`. As a bare minimum
|
|
|
|
|
however, a `javax.sql.DataSource` must be present in the test's `ApplicationContext`.
|
|
|
|
|
|
|
|
|
|
If the algorithms used by `SqlScriptsTestExecutionListener` to detect a `DataSource` and
|
|
|
|
|
`PlatformTransactionManager` and infer the transaction semantics do not suit your needs,
|
|
|
|
|
you may specify explicit names via the `dataSource` and `transactionManager` attributes
|
|
|
|
|
of `@SqlConfig`. Furthermore, the transaction propagation behavior can be controlled via
|
|
|
|
|
the `transactionMode` attribute of `@SqlConfig` -- for example, if scripts should be
|
|
|
|
|
executed in an isolated transaction. Although a thorough discussion of all supported
|
|
|
|
|
options for transaction management with `@Sql` is beyond the scope of this reference
|
|
|
|
|
manual, the javadocs for `@SqlConfig` and `SqlScriptsTestExecutionListener` provide
|
|
|
|
|
detailed information, and the following example demonstrates a typical testing scenario
|
2017-09-28 05:27:44 +08:00
|
|
|
|
using JUnit Jupiter and transactional tests with `@Sql`. Note that there is no need to
|
|
|
|
|
clean up the database after the `usersTest()` method is executed since any changes made
|
|
|
|
|
to the database (either within the test method or within the `/test-data.sql` script)
|
|
|
|
|
will be automatically rolled back by the `TransactionalTestExecutionListener` (see
|
2015-03-03 18:38:01 +08:00
|
|
|
|
<<testcontext-tx,transaction management>> for details).
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2017-09-28 05:27:44 +08:00
|
|
|
|
@SpringJUnitConfig(TestDatabaseConfig.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@Transactional
|
2017-09-28 05:27:44 +08:00
|
|
|
|
class TransactionalSqlScriptsTests {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-09-28 05:27:44 +08:00
|
|
|
|
final JdbcTemplate jdbcTemplate;
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
@Autowired
|
2017-09-28 05:27:44 +08:00
|
|
|
|
TransactionalSqlScriptsTests(DataSource dataSource) {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@Sql("/test-data.sql")
|
2017-09-28 05:27:44 +08:00
|
|
|
|
void usersTest() {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
// verify state in test database:
|
|
|
|
|
assertNumUsers(2);
|
|
|
|
|
// execute code that uses the test data...
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-28 05:27:44 +08:00
|
|
|
|
int countRowsInTable(String tableName) {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-28 05:27:44 +08:00
|
|
|
|
void assertNumUsers(int expected) {
|
|
|
|
|
assertEquals(expected, countRowsInTable("user"),
|
|
|
|
|
"Number of rows in the [user] table.");
|
2015-03-03 18:38:01 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
2016-09-06 01:03:26 +08:00
|
|
|
|
[[testcontext-parallel-test-execution]]
|
2016-09-06 00:58:39 +08:00
|
|
|
|
==== Parallel test execution
|
|
|
|
|
|
|
|
|
|
Spring Framework 5.0 introduces basic support for executing tests in parallel within a
|
|
|
|
|
single JVM when using the _Spring TestContext Framework_. In general this means that most
|
|
|
|
|
test classes or test methods can be executed in parallel without any changes to test code
|
|
|
|
|
or configuration.
|
|
|
|
|
|
|
|
|
|
[TIP]
|
|
|
|
|
====
|
|
|
|
|
For details on how to set up parallel test execution, consult the documentation for your
|
|
|
|
|
testing framework, build tool, or IDE.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Keep in mind that the introduction of concurrency into your test suite can result in
|
|
|
|
|
unexpected side effects, strange runtime behavior, and tests that only fail intermittently
|
|
|
|
|
or seemingly randomly. The Spring Team therefore provides the following general guidelines
|
|
|
|
|
for when __not__ to execute tests in parallel.
|
|
|
|
|
|
|
|
|
|
__Do not execute tests in parallel if:__
|
|
|
|
|
|
|
|
|
|
* Tests make use of Spring's `@DirtiesContext` support.
|
|
|
|
|
* Tests make use of JUnit 4's `@FixMethodOrder` support or any testing framework feature
|
|
|
|
|
that is designed to ensure that test methods execute in a particular order. Note,
|
|
|
|
|
however, that this does not apply if entire test classes are executed in parallel.
|
|
|
|
|
* Tests change the state of shared services or systems such as a database, message broker,
|
|
|
|
|
filesystem, etc. This applies to both in-memory and external systems.
|
|
|
|
|
|
|
|
|
|
[TIP]
|
|
|
|
|
====
|
|
|
|
|
If parallel test execution fails with an exception stating that the `ApplicationContext`
|
|
|
|
|
for the current test is no longer active, this typically means that the
|
|
|
|
|
`ApplicationContext` was removed from the `ContextCache` in a different thread.
|
|
|
|
|
|
|
|
|
|
This may be due to the use of `@DirtiesContext` or due to automatic eviction from the
|
|
|
|
|
`ContextCache`. If `@DirtiesContext` is the culprit, you will either need to find a way
|
|
|
|
|
to avoid using `@DirtiesContext` or exclude such tests from parallel execution. If the
|
|
|
|
|
maximum size of the `ContextCache` has been exceeded, you can increase the maximum size
|
|
|
|
|
of the cache. See the discussion on <<testcontext-ctx-management-caching,context
|
|
|
|
|
caching>> for details.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
[WARNING]
|
|
|
|
|
====
|
|
|
|
|
Parallel test execution in the Spring TestContext Framework is only possible if the
|
|
|
|
|
underlying `TestContext` implementation provides a _copy constructor_ as explained in the
|
|
|
|
|
javadocs for `TestContext`. The `DefaultTestContext` used in Spring provides such a
|
|
|
|
|
constructor; however, if you use a third-party library that provides a custom
|
|
|
|
|
`TestContext` implementation, you will need to verify if it is suitable for parallel test
|
|
|
|
|
execution.
|
|
|
|
|
====
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
[[testcontext-support-classes]]
|
|
|
|
|
==== TestContext Framework support classes
|
|
|
|
|
|
2015-07-30 04:03:44 +08:00
|
|
|
|
|
|
|
|
|
[[testcontext-junit4-runner]]
|
2016-05-04 00:45:47 +08:00
|
|
|
|
===== Spring JUnit 4 Runner
|
2015-07-30 04:03:44 +08:00
|
|
|
|
|
2015-12-20 01:27:34 +08:00
|
|
|
|
The __Spring TestContext Framework__ offers full integration with JUnit 4 through a
|
|
|
|
|
custom runner (supported on JUnit 4.12 or higher). By annotating test classes with
|
2016-02-28 06:02:55 +08:00
|
|
|
|
`@RunWith(SpringJUnit4ClassRunner.class)` or the shorter `@RunWith(SpringRunner.class)`
|
2016-05-04 00:45:47 +08:00
|
|
|
|
variant, developers can implement standard JUnit 4 based unit and integration tests and
|
2016-02-28 06:02:55 +08:00
|
|
|
|
simultaneously reap the benefits of the TestContext framework such as support for loading
|
|
|
|
|
application contexts, dependency injection of test instances, transactional test method
|
|
|
|
|
execution, and so on. If you would like to use the Spring TestContext Framework with an
|
2016-05-04 00:45:47 +08:00
|
|
|
|
alternative runner such as JUnit 4's `Parameterized` or third-party runners such as the
|
2016-02-28 06:02:55 +08:00
|
|
|
|
`MockitoJUnitRunner`, you may optionally use <<testcontext-junit4-rules,Spring's support
|
|
|
|
|
for JUnit rules>> instead.
|
2015-07-30 04:03:44 +08:00
|
|
|
|
|
|
|
|
|
The following code listing displays the minimal requirements for configuring a test class
|
|
|
|
|
to run with the custom Spring `Runner`. `@TestExecutionListeners` is configured with an
|
|
|
|
|
empty list in order to disable the default listeners, which otherwise would require an
|
|
|
|
|
`ApplicationContext` to be configured through `@ContextConfiguration`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-07-30 04:03:44 +08:00
|
|
|
|
@TestExecutionListeners({})
|
|
|
|
|
public class SimpleTest {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testMethod() {
|
|
|
|
|
// execute test logic...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-junit4-rules]]
|
2016-05-04 00:45:47 +08:00
|
|
|
|
===== Spring JUnit 4 Rules
|
2015-07-30 04:03:44 +08:00
|
|
|
|
|
|
|
|
|
The `org.springframework.test.context.junit4.rules` package provides the following JUnit
|
2015-12-20 01:27:34 +08:00
|
|
|
|
4 rules (supported on JUnit 4.12 or higher).
|
2015-07-30 04:03:44 +08:00
|
|
|
|
|
|
|
|
|
* `SpringClassRule`
|
|
|
|
|
* `SpringMethodRule`
|
|
|
|
|
|
|
|
|
|
`SpringClassRule` is a JUnit `TestRule` that supports _class-level_ features of the
|
|
|
|
|
_Spring TestContext Framework_; whereas, `SpringMethodRule` is a JUnit `MethodRule` that
|
|
|
|
|
supports instance-level and method-level features of the _Spring TestContext Framework_.
|
|
|
|
|
|
2016-02-28 06:02:55 +08:00
|
|
|
|
In contrast to the `SpringRunner`, Spring's rule-based JUnit support has the advantage
|
|
|
|
|
that it is independent of any `org.junit.runner.Runner` implementation and can therefore
|
2016-05-04 00:45:47 +08:00
|
|
|
|
be combined with existing alternative runners like JUnit 4's `Parameterized` or third-party
|
2016-02-28 06:02:55 +08:00
|
|
|
|
runners such as the `MockitoJUnitRunner`.
|
2015-07-30 04:03:44 +08:00
|
|
|
|
|
|
|
|
|
In order to support the full functionality of the TestContext framework, a
|
|
|
|
|
`SpringClassRule` must be combined with a `SpringMethodRule`. The following example
|
|
|
|
|
demonstrates the proper way to declare these rules in an integration test.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
// Optionally specify a non-Spring Runner via @RunWith(...)
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
public class IntegrationTest {
|
|
|
|
|
|
|
|
|
|
@ClassRule
|
2017-09-06 23:38:22 +08:00
|
|
|
|
public static final SpringClassRule springClassRule = new SpringClassRule();
|
2015-07-30 04:03:44 +08:00
|
|
|
|
|
|
|
|
|
@Rule
|
|
|
|
|
public final SpringMethodRule springMethodRule = new SpringMethodRule();
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testMethod() {
|
|
|
|
|
// execute test logic...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
[[testcontext-support-classes-junit4]]
|
2016-05-04 00:45:47 +08:00
|
|
|
|
===== JUnit 4 support classes
|
2015-07-30 04:03:44 +08:00
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
The `org.springframework.test.context.junit4` package provides the following support
|
2016-05-04 00:45:47 +08:00
|
|
|
|
classes for JUnit 4 based test cases (supported on JUnit 4.12 or higher).
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
* `AbstractJUnit4SpringContextTests`
|
|
|
|
|
* `AbstractTransactionalJUnit4SpringContextTests`
|
|
|
|
|
|
|
|
|
|
`AbstractJUnit4SpringContextTests` is an abstract base test class that integrates the
|
|
|
|
|
__Spring TestContext Framework__ with explicit `ApplicationContext` testing support in
|
2015-12-20 01:27:34 +08:00
|
|
|
|
a JUnit 4 environment. When you extend `AbstractJUnit4SpringContextTests`, you can
|
2015-03-03 18:38:01 +08:00
|
|
|
|
access a `protected` `applicationContext` instance variable that can be used to perform
|
|
|
|
|
explicit bean lookups or to test the state of the context as a whole.
|
|
|
|
|
|
|
|
|
|
`AbstractTransactionalJUnit4SpringContextTests` is an abstract __transactional__ extension
|
|
|
|
|
of `AbstractJUnit4SpringContextTests` that adds some convenience functionality for JDBC
|
|
|
|
|
access. This class expects a `javax.sql.DataSource` bean and a `PlatformTransactionManager`
|
|
|
|
|
bean to be defined in the `ApplicationContext`. When you extend
|
|
|
|
|
`AbstractTransactionalJUnit4SpringContextTests` you can access a `protected` `jdbcTemplate`
|
|
|
|
|
instance variable that can be used to execute SQL statements to query the database. Such
|
|
|
|
|
queries can be used to confirm database state both __prior to__ and __after__ execution of
|
|
|
|
|
database-related application code, and Spring ensures that such queries run in the scope of
|
|
|
|
|
the same transaction as the application code. When used in conjunction with an ORM tool,
|
|
|
|
|
be sure to avoid <<testcontext-tx-false-positives,false positives>>. As mentioned in
|
|
|
|
|
<<integration-testing-support-jdbc>>, `AbstractTransactionalJUnit4SpringContextTests`
|
|
|
|
|
also provides convenience methods which delegate to methods in `JdbcTestUtils` using the
|
|
|
|
|
aforementioned `jdbcTemplate`. Furthermore, `AbstractTransactionalJUnit4SpringContextTests`
|
|
|
|
|
provides an `executeSqlScript(..)` method for executing SQL scripts against the configured
|
|
|
|
|
`DataSource`.
|
|
|
|
|
|
|
|
|
|
[TIP]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
These classes are a convenience for extension. If you do not want your test classes to be
|
|
|
|
|
tied to a Spring-specific class hierarchy, you can configure your own custom test classes
|
2016-02-28 06:02:55 +08:00
|
|
|
|
by using `@RunWith(SpringRunner.class)` or <<testcontext-junit4-rules,Spring's
|
2015-07-30 04:03:44 +08:00
|
|
|
|
JUnit rules>>.
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
2017-09-28 05:01:59 +08:00
|
|
|
|
[[testcontext-junit-jupiter-extension]]
|
|
|
|
|
===== SpringExtension for JUnit Jupiter
|
|
|
|
|
|
|
|
|
|
The __Spring TestContext Framework__ offers full integration with the _JUnit Jupiter_
|
|
|
|
|
testing framework introduced in JUnit 5. By annotating test classes with
|
|
|
|
|
`@ExtendWith(SpringExtension.class)`, developers can implement standard JUnit Jupiter
|
|
|
|
|
based unit and integration tests and simultaneously reap the benefits of the TestContext
|
|
|
|
|
framework such as support for loading application contexts, dependency injection of test
|
|
|
|
|
instances, transactional test method execution, and so on.
|
|
|
|
|
|
|
|
|
|
Furthermore, thanks to the rich extension API in JUnit Jupiter, Spring is able to provide
|
|
|
|
|
the following features above and beyond the feature set that Spring supports for JUnit 4
|
|
|
|
|
and TestNG.
|
|
|
|
|
|
|
|
|
|
* Dependency injection for test constructors, test methods, and test lifecycle callback
|
|
|
|
|
methods
|
|
|
|
|
- See <<testcontext-junit-jupiter-di>> for further details.
|
|
|
|
|
* Powerful support for link:http://junit.org/junit5/docs/current/user-guide/#extensions-conditions[_conditional test execution_]
|
|
|
|
|
based on SpEL expressions, environment variables, system properties, etc.
|
|
|
|
|
- See the documentation for `@EnabledIf` and `@DisabledIf` in
|
|
|
|
|
<<integration-testing-annotations-junit-jupiter>> for further details and examples.
|
|
|
|
|
* Custom _composed annotations_ that combine annotations from Spring **and** JUnit
|
|
|
|
|
Jupiter.
|
|
|
|
|
- See the `@TransactionalDevTestConfig` and `@TransactionalIntegrationTest` examples in
|
|
|
|
|
<<integration-testing-annotations-meta>> for further details.
|
|
|
|
|
|
|
|
|
|
The following code listing demonstrates how to configure a test class to use the
|
|
|
|
|
`SpringExtension` in conjunction with `@ContextConfiguration`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
// Instructs JUnit Jupiter to extend the test with Spring support.
|
|
|
|
|
@ExtendWith(SpringExtension.class)
|
|
|
|
|
// Instructs Spring to load an ApplicationContext from TestConfig.class
|
|
|
|
|
@ContextConfiguration(classes = TestConfig.class)
|
|
|
|
|
class SimpleTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void testMethod() {
|
|
|
|
|
// execute test logic...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Since annotations in JUnit 5 can also be used as meta-annotations, Spring is able to
|
|
|
|
|
provide `@SpringJUnitConfig` and `@SpringJUnitWebConfig` __composed annotations__ to
|
|
|
|
|
simplify the configuration of the test `ApplicationContext` and JUnit Jupiter.
|
|
|
|
|
|
|
|
|
|
For example, the following example uses `@SpringJUnitConfig` to reduce the amount of
|
|
|
|
|
configuration used in the previous example.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
// Instructs Spring to register the SpringExtension with JUnit
|
|
|
|
|
// Jupiter and load an ApplicationContext from TestConfig.class
|
|
|
|
|
@SpringJUnitConfig(TestConfig.class)
|
|
|
|
|
class SimpleTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void testMethod() {
|
|
|
|
|
// execute test logic...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Similarly, the following example uses `@SpringJUnitWebConfig` to create a
|
|
|
|
|
`WebApplicationContext` for use with JUnit Jupiter.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
// Instructs Spring to register the SpringExtension with JUnit
|
|
|
|
|
// Jupiter and load a WebApplicationContext from TestWebConfig.class
|
|
|
|
|
@SpringJUnitWebConfig(TestWebConfig.class)
|
|
|
|
|
class SimpleWebTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void testMethod() {
|
|
|
|
|
// execute test logic...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
See the documentation for `@SpringJUnitConfig` and `@SpringJUnitWebConfig` in
|
|
|
|
|
<<integration-testing-annotations-junit-jupiter>> for further details.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testcontext-junit-jupiter-di]]
|
|
|
|
|
===== Dependency Injection with the SpringExtension
|
|
|
|
|
|
|
|
|
|
The `SpringExtension` implements the
|
|
|
|
|
link:http://junit.org/junit5/docs/current/user-guide/#extensions-parameter-resolution[`ParameterResolver`]
|
|
|
|
|
extension API from JUnit Jupiter which allows Spring to provide dependency injection for
|
|
|
|
|
test constructors, test methods, and test lifecycle callback methods.
|
|
|
|
|
|
|
|
|
|
Specifically, the `SpringExtension` is able to inject dependencies from the test's
|
|
|
|
|
`ApplicationContext` into test constructors and methods annotated with `@BeforeAll`,
|
|
|
|
|
`@AfterAll`, `@BeforeEach`, `@AfterEach`, `@Test`, `@RepeatedTest`, `@ParameterizedTest`,
|
|
|
|
|
etc.
|
|
|
|
|
|
|
|
|
|
[[testcontext-junit-jupiter-di-constructor]]
|
|
|
|
|
====== Constructor Injection
|
|
|
|
|
|
|
|
|
|
If a parameter in a constructor for a JUnit Jupiter test class is of type
|
|
|
|
|
`ApplicationContext` (or a sub-type thereof) or is annotated or meta-annotated with
|
|
|
|
|
`@Autowired`, `@Qualifier`, or `@Value`, Spring will inject the value for that specific
|
|
|
|
|
parameter with the corresponding bean from the test's `ApplicationContext`. A test
|
|
|
|
|
constructor can also be directly annotated with `@Autowired` if all of the parameters
|
|
|
|
|
should be supplied by Spring.
|
|
|
|
|
|
|
|
|
|
[WARNING]
|
|
|
|
|
====
|
|
|
|
|
If the constructor for a test class is itself annotated with `@Autowired`, Spring will
|
|
|
|
|
assume the responsibility for resolving **all** parameters in the constructor.
|
|
|
|
|
Consequently, no other `ParameterResolver` registered with JUnit Jupiter will be able to
|
|
|
|
|
resolve parameters for such a constructor.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
In the following example, Spring will inject the `OrderService` bean from the
|
|
|
|
|
`ApplicationContext` loaded from `TestConfig.class` into the
|
|
|
|
|
`OrderServiceIntegrationTests` constructor. Note as well that this feature allows test
|
|
|
|
|
dependencies to be `final` and therefore _immutable_.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(TestConfig.class)
|
|
|
|
|
class OrderServiceIntegrationTests {
|
|
|
|
|
|
|
|
|
|
private final OrderService orderService;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
OrderServiceIntegrationTests(OrderService orderService) {
|
|
|
|
|
this.orderService = orderService.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tests that use the injected OrderService
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[testcontext-junit-jupiter-di-method]]
|
|
|
|
|
====== Method Injection
|
|
|
|
|
|
|
|
|
|
If a parameter in a JUnit Jupiter test method or test lifecycle callback method is of
|
|
|
|
|
type `ApplicationContext` (or a sub-type thereof) or is annotated or meta-annotated with
|
|
|
|
|
`@Autowired`, `@Qualifier`, or `@Value`, Spring will inject the value for that specific
|
|
|
|
|
parameter with the corresponding bean from the test's `ApplicationContext`.
|
|
|
|
|
|
|
|
|
|
In the following example, Spring will inject the `OrderService` from the
|
|
|
|
|
`ApplicationContext` loaded from `TestConfig.class` into the `deleteOrder()` test method.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(TestConfig.class)
|
|
|
|
|
class OrderServiceIntegrationTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void deleteOrder(@Autowired OrderService orderService) {
|
|
|
|
|
// use orderService from the test's ApplicationContext
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Due to the robustness of the `ParameterResolver` support in JUnit Jupiter, it is also
|
|
|
|
|
possible to have multiple dependencies injected into a single method not only from Spring
|
|
|
|
|
but also from JUnit Jupiter itself or other third-party extensions.
|
|
|
|
|
|
|
|
|
|
The following example demonstrates how to have both Spring and JUnit Jupiter inject
|
|
|
|
|
dependencies into the `placeOrderRepeatedly()` test method simultaneously. Note that the
|
|
|
|
|
use of `@RepeatedTest` from JUnit Jupiter allows the test method to gain access to the
|
|
|
|
|
`RepetitionInfo`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(TestConfig.class)
|
|
|
|
|
class OrderServiceIntegrationTests {
|
|
|
|
|
|
|
|
|
|
@RepeatedTest(10)
|
2017-09-28 05:27:44 +08:00
|
|
|
|
void placeOrderRepeatedly(RepetitionInfo repetitionInfo,
|
|
|
|
|
@Autowired OrderService orderService) {
|
2017-09-28 05:01:59 +08:00
|
|
|
|
|
|
|
|
|
// use orderService from the test's ApplicationContext
|
|
|
|
|
// and repetitionInfo from JUnit Jupiter
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
[[testcontext-support-classes-testng]]
|
|
|
|
|
===== TestNG support classes
|
2015-07-30 04:03:44 +08:00
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
The `org.springframework.test.context.testng` package provides the following support
|
|
|
|
|
classes for TestNG based test cases.
|
|
|
|
|
|
|
|
|
|
* `AbstractTestNGSpringContextTests`
|
|
|
|
|
* `AbstractTransactionalTestNGSpringContextTests`
|
|
|
|
|
|
|
|
|
|
`AbstractTestNGSpringContextTests` is an abstract base test class that integrates the
|
|
|
|
|
__Spring TestContext Framework__ with explicit `ApplicationContext` testing support in
|
|
|
|
|
a TestNG environment. When you extend `AbstractTestNGSpringContextTests`, you can
|
|
|
|
|
access a `protected` `applicationContext` instance variable that can be used to perform
|
|
|
|
|
explicit bean lookups or to test the state of the context as a whole.
|
|
|
|
|
|
|
|
|
|
`AbstractTransactionalTestNGSpringContextTests` is an abstract __transactional__ extension
|
|
|
|
|
of `AbstractTestNGSpringContextTests` that adds some convenience functionality for JDBC
|
|
|
|
|
access. This class expects a `javax.sql.DataSource` bean and a `PlatformTransactionManager`
|
|
|
|
|
bean to be defined in the `ApplicationContext`. When you extend
|
|
|
|
|
`AbstractTransactionalTestNGSpringContextTests` you can access a `protected` `jdbcTemplate`
|
|
|
|
|
instance variable that can be used to execute SQL statements to query the database. Such
|
|
|
|
|
queries can be used to confirm database state both __prior to__ and __after__ execution of
|
|
|
|
|
database-related application code, and Spring ensures that such queries run in the scope of
|
|
|
|
|
the same transaction as the application code. When used in conjunction with an ORM tool,
|
|
|
|
|
be sure to avoid <<testcontext-tx-false-positives,false positives>>. As mentioned in
|
|
|
|
|
<<integration-testing-support-jdbc>>, `AbstractTransactionalTestNGSpringContextTests`
|
|
|
|
|
also provides convenience methods which delegate to methods in `JdbcTestUtils` using the
|
|
|
|
|
aforementioned `jdbcTemplate`. Furthermore, `AbstractTransactionalTestNGSpringContextTests`
|
|
|
|
|
provides an `executeSqlScript(..)` method for executing SQL scripts against the configured
|
|
|
|
|
`DataSource`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[TIP]
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
These classes are a convenience for extension. If you do not want your test classes to be
|
|
|
|
|
tied to a Spring-specific class hierarchy, you can configure your own custom test classes
|
|
|
|
|
by using `@ContextConfiguration`, `@TestExecutionListeners`, and so on, and by manually
|
|
|
|
|
instrumenting your test class with a `TestContextManager`. See the source code of
|
|
|
|
|
`AbstractTestNGSpringContextTests` for an example of how to instrument your test class.
|
2015-03-07 14:30:52 +08:00
|
|
|
|
====
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-framework]]
|
|
|
|
|
=== Spring MVC Test Framework
|
|
|
|
|
|
2015-10-08 22:56:31 +08:00
|
|
|
|
The __Spring MVC Test framework__ provides first class support for testing Spring MVC
|
|
|
|
|
code using a fluent API that can be used with JUnit, TestNG, or any other testing
|
|
|
|
|
framework. It's built on the
|
2015-10-27 21:31:00 +08:00
|
|
|
|
{api-spring-framework}/mock/web/package-summary.html[Servlet API mock objects]
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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.
|
2015-07-07 10:27:19 +08:00
|
|
|
|
|
|
|
|
|
__Spring MVC Test__ also provides client-side support for testing code that uses
|
2015-10-08 22:57:31 +08:00
|
|
|
|
the `RestTemplate`. Client-side tests mock the server responses and also do _not_
|
|
|
|
|
use a running server.
|
2015-07-07 10:27:19 +08:00
|
|
|
|
|
2015-07-07 11:06:40 +08:00
|
|
|
|
[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
|
2015-10-27 21:31:00 +08:00
|
|
|
|
{doc-spring-boot}/html/boot-features-testing.html#boot-features-testing-spring-boot-applications[Spring Boot reference page].
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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>>.
|
2015-07-07 11:06:40 +08:00
|
|
|
|
====
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server]]
|
|
|
|
|
==== Server-Side Tests
|
2015-10-08 22:56:31 +08:00
|
|
|
|
It's easy to write a plain unit test for a Spring MVC controller using JUnit or TestNG:
|
|
|
|
|
simply instantiate the controller, inject it with mocked or stubbed dependencies, and call
|
|
|
|
|
its methods passing `MockHttpServletRequest`, `MockHttpServletResponse`, etc., as necessary.
|
|
|
|
|
However, when writing such a unit test, much remains untested: for example, request
|
|
|
|
|
mappings, data binding, type conversion, validation, and much more. Furthermore, other
|
|
|
|
|
controller methods such as `@InitBinder`, `@ModelAttribute`, and `@ExceptionHandler` may
|
|
|
|
|
also be invoked as part of the request processing lifecycle.
|
2015-07-07 10:27:19 +08:00
|
|
|
|
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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`.
|
2015-07-07 10:27:19 +08:00
|
|
|
|
|
2015-07-22 00:26:03 +08:00
|
|
|
|
__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
|
|
|
|
|
requests and generating responses without the need for running in a Servlet container.
|
|
|
|
|
For the most part everything should work as it does at runtime with a few notable
|
2015-10-08 22:56:31 +08:00
|
|
|
|
exceptions as explained in <<spring-mvc-test-vs-end-to-end-integration-tests>>. Here is a
|
2017-09-28 05:27:44 +08:00
|
|
|
|
JUnit Jupiter based example of using Spring MVC Test:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
----
|
2017-09-28 05:27:44 +08:00
|
|
|
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
|
|
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-09-28 05:27:44 +08:00
|
|
|
|
@SpringJUnitWebConfig(locations = "test-servlet-context.xml")
|
|
|
|
|
class ExampleTests {
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-09-28 05:27:44 +08:00
|
|
|
|
private MockMvc mockMvc;
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-09-28 05:27:44 +08:00
|
|
|
|
@BeforeEach
|
|
|
|
|
void setup(WebApplicationContext wac) {
|
|
|
|
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
|
|
|
|
|
}
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-09-28 05:27:44 +08:00
|
|
|
|
@Test
|
|
|
|
|
void getAccount() throws Exception {
|
|
|
|
|
this.mockMvc.perform(get("/accounts/1")
|
|
|
|
|
.accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
|
|
|
|
|
.andExpect(status().isOk())
|
|
|
|
|
.andExpect(content().contentType("application/json"))
|
|
|
|
|
.andExpect(jsonPath("$.name").value("Lee"));
|
|
|
|
|
}
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-09-28 05:27:44 +08:00
|
|
|
|
}
|
2015-03-03 18:38:01 +08:00
|
|
|
|
----
|
|
|
|
|
|
2015-07-07 10:27:19 +08:00
|
|
|
|
The above test relies on the `WebApplicationContext` support of the __TestContext framework__
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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
|
2015-07-07 10:27:19 +08:00
|
|
|
|
https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context[sample tests].
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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
|
2015-03-03 18:38:01 +08:00
|
|
|
|
project]. There are lots of other options for verifying the result of the performed
|
2015-07-07 10:27:19 +08:00
|
|
|
|
request that will be discussed below.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-static-imports]]
|
|
|
|
|
===== Static Imports
|
|
|
|
|
The fluent API in the example above requires a few static imports such as
|
|
|
|
|
`MockMvcRequestBuilders.{asterisk}`, `MockMvcResultMatchers.{asterisk}`,
|
|
|
|
|
and `MockMvcBuilders.{asterisk}`. An easy way to find these classes is to search for
|
|
|
|
|
types matching __"MockMvc*"__. If using Eclipse, be sure to add them as
|
|
|
|
|
"favorite static members" in the Eclipse preferences under
|
|
|
|
|
__Java -> Editor -> Content Assist -> Favorites__. That will allow use of content
|
|
|
|
|
assist after typing the first character of the static method name. Other IDEs (e.g.
|
|
|
|
|
IntelliJ) may not require any additional configuration. Just check the support for code
|
|
|
|
|
completion on static members.
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-setup-options]]
|
2017-01-26 01:32:07 +08:00
|
|
|
|
===== Setup Choices
|
2015-07-07 10:27:19 +08:00
|
|
|
|
There are two main options for creating an instance of `MockMvc`.
|
|
|
|
|
The first is to load Spring MVC configuration through the __TestContext
|
2015-03-03 18:38:01 +08:00
|
|
|
|
framework__, which loads the Spring configuration and injects a `WebApplicationContext`
|
2015-10-08 22:57:31 +08:00
|
|
|
|
into the test to use to build a `MockMvc` instance:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@WebAppConfiguration
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration("my-servlet-context.xml")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class MyWebTests {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private WebApplicationContext wac;
|
|
|
|
|
|
|
|
|
|
private MockMvc mockMvc;
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
|
|
|
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-07 10:27:19 +08:00
|
|
|
|
The second is to simply create a controller instance manually without loading Spring
|
|
|
|
|
configuration. Instead basic default configuration, roughly comparable to that of
|
|
|
|
|
the MVC JavaConfig or the MVC namespace, is automatically created and can be customized
|
|
|
|
|
to a degree:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
public class MyWebTests {
|
|
|
|
|
|
|
|
|
|
private MockMvc mockMvc;
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
|
|
|
|
this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-07 10:27:19 +08:00
|
|
|
|
Which setup option should you use?
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2015-08-26 21:13:19 +08:00
|
|
|
|
The __"webAppContextSetup"__ loads your actual Spring MVC configuration resulting in a
|
2015-03-03 18:38:01 +08:00
|
|
|
|
more complete integration test. Since the __TestContext framework__ caches the loaded
|
2015-08-26 21:13:19 +08:00
|
|
|
|
Spring configuration, it helps keep tests running fast, even as you introduce more tests
|
|
|
|
|
in your test suite. Furthermore, you can inject mock services into controllers through
|
|
|
|
|
Spring configuration in order to remain focused on testing the web layer. Here is an
|
|
|
|
|
example of declaring a mock service with Mockito:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,xml,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
<bean id="accountService" class="org.mockito.Mockito" factory-method="mock">
|
|
|
|
|
<constructor-arg value="org.example.AccountService"/>
|
|
|
|
|
</bean>
|
|
|
|
|
----
|
|
|
|
|
|
2015-08-26 21:13:19 +08:00
|
|
|
|
You can then inject the mock service into the test in order set up and verify
|
2015-03-03 18:38:01 +08:00
|
|
|
|
expectations:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-02-28 06:02:55 +08:00
|
|
|
|
@RunWith(SpringRunner.class)
|
2015-03-03 18:38:01 +08:00
|
|
|
|
@WebAppConfiguration
|
2016-08-26 19:57:18 +08:00
|
|
|
|
@ContextConfiguration("test-servlet-context.xml")
|
2015-03-03 18:38:01 +08:00
|
|
|
|
public class AccountTests {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private WebApplicationContext wac;
|
|
|
|
|
|
|
|
|
|
private MockMvc mockMvc;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private AccountService accountService;
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
The __"standaloneSetup"__ on the other hand is a little closer to a unit test. It tests
|
2015-08-26 21:13:19 +08:00
|
|
|
|
one controller at a time: the controller can be injected with mock dependencies manually,
|
|
|
|
|
and it doesn't involve loading Spring configuration. Such tests are more focused on style
|
|
|
|
|
and make it easier to see which controller is being tested, whether any specific Spring
|
|
|
|
|
MVC configuration is required to work, and so on. The "standaloneSetup" is also a very
|
|
|
|
|
convenient way to write ad-hoc tests to verify specific behavior or to debug an issue.
|
|
|
|
|
|
|
|
|
|
Just like with any "integration vs. unit testing" debate, there is no right or wrong
|
|
|
|
|
answer. However, using the "standaloneSetup" does imply the need for additional
|
|
|
|
|
"webAppContextSetup" tests in order to verify your Spring MVC configuration.
|
|
|
|
|
Alternatively, you may choose to write all tests with "webAppContextSetup" in order to
|
|
|
|
|
always test against your actual Spring MVC configuration.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2017-01-26 01:32:07 +08:00
|
|
|
|
[[spring-mvc-test-server-setup-steps]]
|
|
|
|
|
===== Setup Features
|
|
|
|
|
|
|
|
|
|
No matter which MockMvc builder you use all `MockMvcBuilder` implementations provide
|
|
|
|
|
some common and very useful features. For example you can declare an `Accept` header
|
|
|
|
|
for all requests and expect a status of 200 as well as a `Content-Type` header
|
|
|
|
|
in all responses as follows:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
// static import of MockMvcBuilders.standaloneSetup
|
|
|
|
|
|
|
|
|
|
MockMVc mockMvc = standaloneSetup(new MusicController())
|
|
|
|
|
.defaultRequest(get("/").accept(MediaType.APPLICATION_JSON))
|
|
|
|
|
.alwaysExpect(status().isOk())
|
|
|
|
|
.alwaysExpect(content().contentType("application/json;charset=UTF-8"))
|
|
|
|
|
.build();
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
In addition 3rd party frameworks (and applications) may pre-package setup
|
|
|
|
|
instructions like the ones through a `MockMvcConfigurer`. The Spring Framework
|
|
|
|
|
has one such built-in implementation that helps to save and re-use the HTTP
|
|
|
|
|
session across requests. It can be used as follows:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
// static import of SharedHttpSessionConfigurer.sharedHttpSession
|
|
|
|
|
|
|
|
|
|
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController())
|
|
|
|
|
.apply(sharedHttpSession())
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
// Use mockMvc to perform requests...
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
See `ConfigurableMockMvcBuilder` for a list of all MockMvc builder features
|
|
|
|
|
or use the IDE to explore the available options.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
[[spring-mvc-test-server-performing-requests]]
|
|
|
|
|
===== Performing Requests
|
2015-07-07 10:27:19 +08:00
|
|
|
|
It's easy to perform requests using any HTTP method:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-07 10:27:19 +08:00
|
|
|
|
You can also perform file upload requests that internally use
|
|
|
|
|
`MockMultipartHttpServletRequest` so that there is no actual parsing of a multipart
|
|
|
|
|
request but rather you have to set it up:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2017-01-26 06:04:25 +08:00
|
|
|
|
mockMvc.perform(multipart("/doc").file("a1", "ABC".getBytes("UTF-8")));
|
2015-03-03 18:38:01 +08:00
|
|
|
|
----
|
|
|
|
|
|
2015-07-07 10:27:19 +08:00
|
|
|
|
You can specify query parameters in URI template style:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
mockMvc.perform(get("/hotels?foo={foo}", "bar"));
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-07 10:27:19 +08:00
|
|
|
|
Or you can add Servlet request parameters representing either query of form parameters:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
mockMvc.perform(get("/hotels").param("foo", "bar"));
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-07 10:27:19 +08:00
|
|
|
|
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
|
2015-10-08 22:57:31 +08:00
|
|
|
|
request parameters provided through the `param(...)` method are expected to already be decoded.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Looking at the above example, it would be cumbersome to set the contextPath and
|
2015-07-07 10:27:19 +08:00
|
|
|
|
servletPath with every performed request. Instead you can set up default request
|
|
|
|
|
properties:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
public class MyWebTests {
|
|
|
|
|
|
|
|
|
|
private MockMvc mockMvc;
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
|
|
|
|
mockMvc = standaloneSetup(new AccountController())
|
|
|
|
|
.defaultRequest(get("/")
|
|
|
|
|
.contextPath("/app").servletPath("/main")
|
|
|
|
|
.accept(MediaType.APPLICATION_JSON).build();
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-07 10:27:19 +08:00
|
|
|
|
The above properties will affect every request performed through the `MockMvc` instance.
|
|
|
|
|
If the same property is also specified on a given request, it overrides the default value.
|
2015-08-26 21:13:19 +08:00
|
|
|
|
That is why the HTTP method and URI in the default request don't matter since they must be
|
2015-07-07 10:27:19 +08:00
|
|
|
|
specified on every request.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-defining-expectations]]
|
|
|
|
|
===== Defining Expectations
|
2015-07-07 10:27:19 +08:00
|
|
|
|
Expectations can be defined by appending one or more `.andExpect(..)` calls after
|
|
|
|
|
performing a request:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
|
|
|
|
|
----
|
|
|
|
|
|
2015-10-08 22:57:31 +08:00
|
|
|
|
`MockMvcResultMatchers.*` provides a number of expectations, some of which are further
|
2015-07-07 10:27:19 +08:00
|
|
|
|
nested with more detailed expectations.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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
|
2015-07-07 10:27:19 +08:00
|
|
|
|
are the most important results to assert.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
mockMvc.perform(post("/persons"))
|
|
|
|
|
.andExpect(status().isOk())
|
|
|
|
|
.andExpect(model().attributeHasErrors("person"));
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-03 03:47:14 +08:00
|
|
|
|
Many times when writing tests, it's useful to _dump_ the results of the performed request.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
This can be done as follows, where `print()` is a static import from
|
|
|
|
|
`MockMvcResultHandlers`:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
mockMvc.perform(post("/persons"))
|
|
|
|
|
.andDo(print())
|
|
|
|
|
.andExpect(status().isOk())
|
|
|
|
|
.andExpect(model().attributeHasErrors("person"));
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-03 03:47:14 +08:00
|
|
|
|
As long as request processing does not cause an unhandled exception, the `print()` method
|
2016-04-06 20:45:28 +08:00
|
|
|
|
will print all the available result data to `System.out`. Spring Framework 4.2 introduced
|
2015-10-08 22:57:31 +08:00
|
|
|
|
a `log()` method and two additional variants of the `print()` method, one that accepts
|
2015-07-03 03:47:14 +08:00
|
|
|
|
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
|
|
|
|
|
have the result data _logged_ instead of printed, simply invoke the `log()` method which
|
|
|
|
|
will log the result data as a single `DEBUG` message under the
|
|
|
|
|
`org.springframework.test.web.servlet.result` logging category.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
In some cases, you may want to get direct access to the result and verify something that
|
2015-10-08 22:57:31 +08:00
|
|
|
|
cannot be verified otherwise. This can be achieved by appending `.andReturn()` after all
|
|
|
|
|
other expectations:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn();
|
|
|
|
|
// ...
|
|
|
|
|
----
|
|
|
|
|
|
2015-10-08 22:57:31 +08:00
|
|
|
|
If all tests repeat the same expectations you can set up common expectations once
|
2015-07-07 10:27:19 +08:00
|
|
|
|
when building the `MockMvc` instance:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
standaloneSetup(new SimpleController())
|
|
|
|
|
.alwaysExpect(status().isOk())
|
|
|
|
|
.alwaysExpect(content().contentType("application/json;charset=UTF-8"))
|
|
|
|
|
.build()
|
|
|
|
|
----
|
|
|
|
|
|
2015-10-08 22:57:31 +08:00
|
|
|
|
Note that common expectations are __always__ applied and cannot be overridden without
|
2015-03-03 18:38:01 +08:00
|
|
|
|
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
|
2015-10-08 22:57:31 +08:00
|
|
|
|
be verified using JsonPath expressions:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON))
|
2016-04-06 20:14:51 +08:00
|
|
|
|
.andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people"));
|
2015-03-03 18:38:01 +08:00
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
When XML response content contains hypermedia links created with
|
|
|
|
|
https://github.com/spring-projects/spring-hateoas[Spring HATEOAS], the resulting links can
|
2015-10-08 22:57:31 +08:00
|
|
|
|
be verified using XPath expressions:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
Map<String, String> ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom");
|
|
|
|
|
mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML))
|
2016-04-06 20:14:51 +08:00
|
|
|
|
.andExpect(xpath("/person/ns:link[@rel='self']/@href", ns).string("http://localhost:8080/people"));
|
2015-03-03 18:38:01 +08:00
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-filters]]
|
|
|
|
|
===== Filter Registrations
|
2015-10-08 22:57:31 +08:00
|
|
|
|
When setting up a `MockMvc` instance, you can register one or more Servlet `Filter` instances:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();
|
|
|
|
|
----
|
|
|
|
|
|
2015-10-08 22:57:31 +08:00
|
|
|
|
Registered filters will be invoked through via the `MockFilterChain` from `spring-test`, and the
|
|
|
|
|
last filter will delegate to the `DispatcherServlet`.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2015-07-07 11:06:40 +08:00
|
|
|
|
[[spring-mvc-test-vs-end-to-end-integration-tests]]
|
2015-10-08 22:57:31 +08:00
|
|
|
|
===== Differences between Out-of-Container and End-to-End Integration Tests
|
2015-07-07 11:06:40 +08:00
|
|
|
|
|
|
|
|
|
As mentioned earlier __Spring MVC Test__ is built on the Servlet API mock objects from
|
2015-10-08 22:57:31 +08:00
|
|
|
|
the `spring-test` module and does not use a running Servlet container. Therefore
|
2015-07-07 11:06:40 +08:00
|
|
|
|
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`.
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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.
|
2015-07-07 11:06:40 +08:00
|
|
|
|
|
|
|
|
|
This means if you are using JSPs you can verify the JSP page to which the request was
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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
|
2016-07-05 05:30:58 +08:00
|
|
|
|
forwarding such as Thymeleaf and Freemarker will render HTML to the response body as
|
|
|
|
|
expected. The same is true for rendering JSON, XML, and other formats via `@ResponseBody`
|
|
|
|
|
methods.
|
2015-07-07 11:06:40 +08:00
|
|
|
|
|
|
|
|
|
Alternatively you may consider the full end-to-end integration testing support from
|
|
|
|
|
Spring Boot via `@WebIntegrationTest`. See the
|
2015-10-27 21:31:00 +08:00
|
|
|
|
{doc-spring-boot}/html/boot-features-testing.html#boot-features-testing-spring-boot-applications[Spring Boot reference].
|
2015-07-07 11:06:40 +08:00
|
|
|
|
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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.
|
2015-07-07 11:06:40 +08:00
|
|
|
|
|
|
|
|
|
Another important distinction when using __Spring MVC Test__ is that conceptually such
|
2015-10-08 22:57:31 +08:00
|
|
|
|
tests are on the _inside_ of the server-side so you can check what handler was used,
|
2015-07-07 11:06:40 +08:00
|
|
|
|
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
|
2015-10-08 22:57:31 +08:00
|
|
|
|
an actual HTTP client. This is generally an advantage of classic unit testing, that it's
|
2015-07-07 11:06:40 +08:00
|
|
|
|
easier to write, reason about, and debug but does not replace the need for full
|
2015-10-08 22:57:31 +08:00
|
|
|
|
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.
|
2015-07-07 11:06:40 +08:00
|
|
|
|
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
[[spring-mvc-test-server-resources]]
|
|
|
|
|
===== Further Server-Side Test Examples
|
|
|
|
|
The framework's own tests include
|
|
|
|
|
https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/servlet/samples[many
|
|
|
|
|
sample tests] intended to demonstrate how to use Spring MVC Test. Browse these examples
|
|
|
|
|
for further ideas. Also the
|
|
|
|
|
https://github.com/spring-projects/spring-mvc-showcase[spring-mvc-showcase] has full test
|
|
|
|
|
coverage based on Spring MVC Test.
|
|
|
|
|
|
|
|
|
|
|
2015-06-24 00:31:48 +08:00
|
|
|
|
[[spring-mvc-test-server-htmlunit]]
|
|
|
|
|
==== HtmlUnit Integration
|
|
|
|
|
|
|
|
|
|
Spring provides integration between <<spring-mvc-test-server,MockMvc>> and
|
2015-07-29 00:46:18 +08:00
|
|
|
|
http://htmlunit.sourceforge.net/[HtmlUnit]. This simplifies performing end-to-end testing
|
2015-06-24 00:31:48 +08:00
|
|
|
|
when using HTML based views. This integration enables developers to:
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
* Easily test HTML pages using tools such as http://htmlunit.sourceforge.net/[HtmlUnit],
|
2015-06-24 00:31:48 +08:00
|
|
|
|
http://seleniumhq.org/projects/webdriver/[WebDriver], &
|
2015-07-29 00:46:18 +08:00
|
|
|
|
http://www.gebish.org/manual/current/testing.html#spock_junit__testng[Geb] without the
|
|
|
|
|
need to deploy to a Servlet container
|
|
|
|
|
* Test JavaScript within pages
|
|
|
|
|
* Optionally test using mock services to speed up testing
|
|
|
|
|
* Share logic between in-container end-to-end tests and out-of-container integration tests
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
2015-07-29 00:46:18 +08:00
|
|
|
|
`MockMvc` works with templating technologies that do not rely on a Servlet Container (e.g.,
|
2016-07-05 05:30:58 +08:00
|
|
|
|
Thymeleaf, FreeMarker, etc.), but it does not work with JSPs since they rely on the Servlet
|
|
|
|
|
container.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-why]]
|
|
|
|
|
===== Why HtmlUnit Integration?
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
The most obvious question that comes to mind is, "Why do I need this?". The answer is best
|
2015-06-24 00:31:48 +08:00
|
|
|
|
found by exploring a very basic sample application. Assume you have a Spring MVC web
|
2015-07-29 00:46:18 +08:00
|
|
|
|
application that supports CRUD operations on a `Message` object. The application also supports
|
2015-06-24 00:31:48 +08:00
|
|
|
|
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`.
|
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
MockHttpServletRequestBuilder createMessage = post("/messages/")
|
|
|
|
|
.param("summary", "Spring Rocks")
|
|
|
|
|
.param("text", "In case you didn't know, Spring Rocks!");
|
|
|
|
|
|
|
|
|
|
mockMvc.perform(createMessage)
|
|
|
|
|
.andExpect(status().is3xxRedirection())
|
|
|
|
|
.andExpect(redirectedUrl("/messages/123"));
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
What if we want to test our form view that allows us to create the message? For example,
|
|
|
|
|
assume our form looks like the following snippet:
|
|
|
|
|
|
|
|
|
|
[source,xml]
|
|
|
|
|
----
|
|
|
|
|
<form id="messageForm" action="/messages/" method="post">
|
|
|
|
|
<div class="pull-right"><a href="/messages/">Messages</a></div>
|
|
|
|
|
|
|
|
|
|
<label for="summary">Summary</label>
|
|
|
|
|
<input type="text" class="required" id="summary" name="summary" value="" />
|
|
|
|
|
|
|
|
|
|
<label for="text">Message</label>
|
|
|
|
|
<textarea id="text" name="text"></textarea>
|
|
|
|
|
|
|
|
|
|
<div class="form-actions">
|
|
|
|
|
<input type="submit" value="Create" />
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
How do we ensure that our form will produce the correct request to create a new message? A
|
|
|
|
|
naive attempt would look like this:
|
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
mockMvc.perform(get("/messages/form"))
|
|
|
|
|
.andExpect(xpath("//input[@name='summary']").exists())
|
|
|
|
|
.andExpect(xpath("//textarea[@name='text']").exists());
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
This test has some obvious drawbacks. If we update our controller to use the parameter
|
|
|
|
|
`message` instead of `text`, our form test would continue to pass even though the HTML
|
|
|
|
|
form is out of synch with the controller. To resolve this we can combine our two tests.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-mock-mvc-test]]
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
String summaryParamName = "summary";
|
|
|
|
|
String textParamName = "text";
|
|
|
|
|
mockMvc.perform(get("/messages/form"))
|
|
|
|
|
.andExpect(xpath("//input[@name='" + summaryParamName + "']").exists())
|
|
|
|
|
.andExpect(xpath("//textarea[@name='" + textParamName + "']").exists());
|
|
|
|
|
|
|
|
|
|
MockHttpServletRequestBuilder createMessage = post("/messages/")
|
|
|
|
|
.param(summaryParamName, "Spring Rocks")
|
|
|
|
|
.param(textParamName, "In case you didn't know, Spring Rocks!");
|
|
|
|
|
|
|
|
|
|
mockMvc.perform(createMessage)
|
|
|
|
|
.andExpect(status().is3xxRedirection())
|
|
|
|
|
.andExpect(redirectedUrl("/messages/123"));
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
This would reduce the risk of our test incorrectly passing, but there are still some
|
2015-07-29 00:46:18 +08:00
|
|
|
|
problems.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
* What if we have multiple forms on our page? Admittedly we could update our xpath
|
|
|
|
|
expressions, but they get more complicated the more factors we take into account (Are the
|
|
|
|
|
fields the correct type? Are the fields enabled? etc.).
|
2015-06-24 00:31:48 +08:00
|
|
|
|
* Another issue is that we are doing double the work we would expect.
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We must first verify the view, and then we submit the view with the same parameters we just
|
|
|
|
|
verified. Ideally this could be done all at once.
|
|
|
|
|
* Finally, there are some things that we still cannot account for. For example, what if the
|
|
|
|
|
form has JavaScript validation that we wish to test as well?
|
|
|
|
|
|
|
|
|
|
The overall problem is that testing a web page does not involve a single interaction.
|
|
|
|
|
Instead, it is a combination of how the user interacts with a web page and how that web
|
|
|
|
|
page interacts with other resources. For example, the result of a form view is used as
|
|
|
|
|
the input to a user for creating a message. In addition, our form view may potentially
|
|
|
|
|
utilize additional resources which impact the behavior of the page, such as JavaScript
|
|
|
|
|
validation.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-why-integration]]
|
|
|
|
|
====== Integration testing to the rescue?
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
To resolve the issues above we could perform end-to-end integration testing, but this has
|
|
|
|
|
some obvious drawbacks. Consider testing the view that allows us to page through the messages.
|
|
|
|
|
We might need the following tests.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
* Does our page display a notification to the user indicating that no results are available
|
2015-06-24 00:31:48 +08:00
|
|
|
|
when the messages are empty?
|
|
|
|
|
* Does our page properly display a single message?
|
|
|
|
|
* Does our page properly support paging?
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
To set up these tests, we would need to ensure our database contained the proper messages
|
|
|
|
|
in it. This leads to a number of additional challenges.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
* Ensuring the proper messages are in the database can be tedious; consider foreign key
|
|
|
|
|
constraints.
|
|
|
|
|
* Testing can become slow since each test would need to ensure that the database is in the
|
|
|
|
|
correct state.
|
|
|
|
|
* Since our database needs to be in a specific state, we cannot run tests in parallel.
|
|
|
|
|
* Performing assertions on things like auto-generated ids, timestamps, etc. can be difficult.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
These challenges do not mean that we should abandon end-to-end integration testing
|
|
|
|
|
altogether. Instead, we can reduce the number of end-to-end integration tests by
|
|
|
|
|
refactoring our detailed tests to use mock services which will execute much faster, more
|
|
|
|
|
reliably, and without side effects. We can then implement a small number of _true_
|
|
|
|
|
end-to-end integration tests that validate simple workflows to ensure that everything
|
|
|
|
|
works together properly.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-why-mockmvc]]
|
|
|
|
|
====== Enter HtmlUnit Integration
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
So how can we achieve a balance between testing the interactions of our pages and still
|
|
|
|
|
retain good performance within our test suite? The answer is: "By integrating MockMvc
|
|
|
|
|
with HtmlUnit."
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-options]]
|
|
|
|
|
====== HtmlUnit Integration Options
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
There are a number of ways to integrate `MockMvc` with HtmlUnit.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
* <<spring-mvc-test-server-htmlunit-mah,MockMvc and HtmlUnit>>: Use this option if you
|
|
|
|
|
want to use the raw HtmlUnit libraries.
|
|
|
|
|
* <<spring-mvc-test-server-htmlunit-webdriver,MockMvc and WebDriver>>: Use this option to
|
|
|
|
|
ease development and reuse code between integration and end-to-end testing.
|
|
|
|
|
* <<spring-mvc-test-server-htmlunit-geb,MockMvc and Geb>>: Use this option if you would
|
|
|
|
|
like to use Groovy for testing, ease development, and reuse code between integration and
|
|
|
|
|
end-to-end testing.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-mah]]
|
|
|
|
|
===== MockMvc and HtmlUnit
|
|
|
|
|
|
|
|
|
|
This section describes how to integrate `MockMvc` and HtmlUnit. Use this option if you
|
|
|
|
|
want to use the raw HtmlUnit libraries.
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-mah-setup]]
|
|
|
|
|
====== MockMvc and HtmlUnit Setup
|
|
|
|
|
|
2015-09-01 06:53:00 +08:00
|
|
|
|
First, make sure that you have included a test dependency on `net.sourceforge.htmlunit:htmlunit`.
|
|
|
|
|
In order to use HtmlUnit with Apache HttpComponents 4.5+, you will need to use HtmlUnit
|
|
|
|
|
2.18 or higher.
|
|
|
|
|
|
2015-06-24 00:31:48 +08:00
|
|
|
|
We can easily create an HtmlUnit `WebClient` that integrates with `MockMvc` using the
|
2015-07-29 00:46:18 +08:00
|
|
|
|
`MockMvcWebClientBuilder` as follows.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
@Autowired
|
|
|
|
|
WebApplicationContext context;
|
|
|
|
|
|
|
|
|
|
WebClient webClient;
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
|
|
|
|
webClient = MockMvcWebClientBuilder
|
|
|
|
|
.webAppContextSetup(context)
|
2015-07-28 01:27:15 +08:00
|
|
|
|
.build();
|
2015-06-24 00:31:48 +08:00
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
|
|
|
|
This is a simple example of using `MockMvcWebClientBuilder`. For advanced usage see
|
|
|
|
|
<<Advanced MockMvcWebClientBuilder>>
|
|
|
|
|
====
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
This will ensure that any URL referencing `localhost` as the server will be directed to
|
|
|
|
|
our `MockMvc` instance without the need for a real HTTP connection. Any other URL will be
|
|
|
|
|
requested using a network connection as normal. This allows us to easily test the use of
|
|
|
|
|
CDNs.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-mah-usage]]
|
|
|
|
|
====== MockMvc and HtmlUnit Usage
|
|
|
|
|
|
|
|
|
|
Now we can use HtmlUnit as we normally would, but without the need to deploy our
|
2015-07-29 00:46:18 +08:00
|
|
|
|
application to a Servlet container. For example, we can request the view to create
|
|
|
|
|
a message with the following.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form");
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
2015-07-29 00:46:18 +08:00
|
|
|
|
The default context path is `""`. Alternatively, we can specify the context path as
|
|
|
|
|
illustrated in <<Advanced MockMvcWebClientBuilder>>.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
====
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
Once we have a reference to the `HtmlPage`, we can then fill out the form and submit
|
|
|
|
|
it to create a message.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");
|
|
|
|
|
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
|
|
|
|
|
summaryInput.setValueAttribute("Spring Rocks");
|
|
|
|
|
HtmlTextArea textInput = createMsgFormPage.getHtmlElementById("text");
|
|
|
|
|
textInput.setText("In case you didn't know, Spring Rocks!");
|
|
|
|
|
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");
|
|
|
|
|
HtmlPage newMessagePage = submit.click();
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
Finally, we can verify that a new message was created successfully. The following
|
2015-10-01 04:06:07 +08:00
|
|
|
|
assertions use the http://joel-costigliola.github.io/assertj/[AssertJ] library.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");
|
|
|
|
|
String id = newMessagePage.getHtmlElementById("id").getTextContent();
|
|
|
|
|
assertThat(id).isEqualTo("123");
|
|
|
|
|
String summary = newMessagePage.getHtmlElementById("summary").getTextContent();
|
|
|
|
|
assertThat(summary).isEqualTo("Spring Rocks");
|
|
|
|
|
String text = newMessagePage.getHtmlElementById("text").getTextContent();
|
|
|
|
|
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
This improves on our <<spring-mvc-test-server-htmlunit-mock-mvc-test,MockMvc test>> in a
|
|
|
|
|
number of ways. First we no longer have to explicitly verify our form and then create a
|
|
|
|
|
request that looks like the form. Instead, we request the form, fill it out, and submit
|
2015-07-29 00:46:18 +08:00
|
|
|
|
it, thereby significantly reducing the overhead.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
Another important factor is that http://htmlunit.sourceforge.net/javascript.html[HtmlUnit
|
|
|
|
|
uses the Mozilla Rhino engine] to evaluate JavaScript. This means that we can test the
|
|
|
|
|
behavior of JavaScript within our pages as well!
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
Refer to the http://htmlunit.sourceforge.net/gettingStarted.html[HtmlUnit documentation]
|
|
|
|
|
for additional information about using HtmlUnit.
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-mah-advanced-builder]]
|
|
|
|
|
====== Advanced MockMvcWebClientBuilder
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
In the examples so far, we have used `MockMvcWebClientBuilder` in the simplest way possible,
|
|
|
|
|
by building a `WebClient` based on the `WebApplicationContext` loaded for us by the Spring
|
|
|
|
|
TestContext Framework. This approach is repeated here.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
@Autowired
|
|
|
|
|
WebApplicationContext context;
|
|
|
|
|
|
|
|
|
|
WebClient webClient;
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
|
|
|
|
webClient = MockMvcWebClientBuilder
|
|
|
|
|
.webAppContextSetup(context)
|
2015-07-28 01:27:15 +08:00
|
|
|
|
.build();
|
2015-06-24 00:31:48 +08:00
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We can also specify additional configuration options.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
2015-07-29 00:46:18 +08:00
|
|
|
|
WebClient webClient;
|
|
|
|
|
|
2015-06-24 00:31:48 +08:00
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
|
|
|
|
webClient = MockMvcWebClientBuilder
|
|
|
|
|
// demonstrates applying a MockMvcConfigurer (Spring Security)
|
|
|
|
|
.webAppContextSetup(context, springSecurity())
|
|
|
|
|
// for illustration only - defaults to ""
|
|
|
|
|
.contextPath("")
|
2015-07-28 01:27:15 +08:00
|
|
|
|
// By default MockMvc is used for localhost only;
|
2015-07-29 00:46:18 +08:00
|
|
|
|
// the following will use MockMvc for example.com and example.org as well
|
2015-06-24 00:31:48 +08:00
|
|
|
|
.useMockMvcForHosts("example.com","example.org")
|
2015-07-28 01:27:15 +08:00
|
|
|
|
.build();
|
2015-06-24 00:31:48 +08:00
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
As an alternative, we can perform the exact same setup by configuring the `MockMvc`
|
|
|
|
|
instance separately and supplying it to the `MockMvcWebClientBuilder` as follows.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
MockMvc mockMvc = MockMvcBuilders
|
|
|
|
|
.webAppContextSetup(context)
|
2015-07-28 04:32:37 +08:00
|
|
|
|
.apply(springSecurity())
|
2015-06-24 00:31:48 +08:00
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
webClient = MockMvcWebClientBuilder
|
|
|
|
|
.mockMvcSetup(mockMvc)
|
|
|
|
|
// for illustration only - defaults to ""
|
|
|
|
|
.contextPath("")
|
2015-07-28 01:27:15 +08:00
|
|
|
|
// By default MockMvc is used for localhost only;
|
2015-07-29 00:46:18 +08:00
|
|
|
|
// the following will use MockMvc for example.com and example.org as well
|
2015-06-24 00:31:48 +08:00
|
|
|
|
.useMockMvcForHosts("example.com","example.org")
|
2015-07-28 01:27:15 +08:00
|
|
|
|
.build();
|
2015-06-24 00:31:48 +08:00
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
This is more verbose, but by building the `WebClient` with a `MockMvc` instance we have
|
2015-07-28 04:32:37 +08:00
|
|
|
|
the full power of `MockMvc` at our fingertips.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[TIP]
|
|
|
|
|
====
|
|
|
|
|
For additional information on creating a `MockMvc` instance refer to
|
|
|
|
|
<<spring-mvc-test-server-setup-options>>.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-webdriver]]
|
|
|
|
|
===== MockMvc and WebDriver
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
In the previous sections, we have seen how to use `MockMvc` in conjunction with the raw
|
|
|
|
|
HtmlUnit APIs. In this section, we will leverage additional abstractions within the Selenium
|
2015-06-24 00:31:48 +08:00
|
|
|
|
http://docs.seleniumhq.org/projects/webdriver/[WebDriver] to make things even easier.
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-webdriver-why]]
|
|
|
|
|
====== Why WebDriver and MockMvc?
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We can already use HtmlUnit and `MockMvc`, so why would we want to use `WebDriver`? The
|
|
|
|
|
Selenium `WebDriver` provides a very elegant API that allows us to easily organize our code.
|
|
|
|
|
To better understand, let's explore an example.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
|
|
|
|
Despite being a part of http://docs.seleniumhq.org/[Selenium], WebDriver does not require
|
|
|
|
|
a Selenium Server to run your tests.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Suppose we need to ensure that a message is created properly. The tests involve finding
|
2015-07-29 00:46:18 +08:00
|
|
|
|
the HTML form input elements, filling them out, and making various assertions.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
This approach results in numerous, separate tests because we want to test error
|
|
|
|
|
conditions as well. For example, we want to ensure that we get an error if we fill out
|
|
|
|
|
only part of the form. If we fill out the entire form, the newly created message should
|
|
|
|
|
be displayed afterwards.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
If one of the fields were named "summary", then we might have something like the
|
|
|
|
|
following repeated in multiple places within our tests.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
|
|
|
|
|
summaryInput.setValueAttribute(summary);
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
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! Of course, this violates the _DRY Principle_; so
|
|
|
|
|
we should ideally extract this code into its own method as follows.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
|
|
|
|
|
setSummary(currentPage, summary);
|
2015-07-29 00:46:18 +08:00
|
|
|
|
// ...
|
2015-06-24 00:31:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setSummary(HtmlPage currentPage, String summary) {
|
|
|
|
|
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
|
|
|
|
|
summaryInput.setValueAttribute(summary);
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
This ensures that we do not have to update all of our tests if we change the UI.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We might even take this a step further and place this logic within an Object that
|
|
|
|
|
represents the `HtmlPage` we are currently on.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
public class CreateMessagePage {
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
final HtmlPage currentPage;
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
final HtmlTextInput summaryInput;
|
|
|
|
|
|
|
|
|
|
final HtmlSubmitInput submit;
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
public CreateMessagePage(HtmlPage currentPage) {
|
|
|
|
|
this.currentPage = currentPage;
|
|
|
|
|
this.summaryInput = currentPage.getHtmlElementById("summary");
|
|
|
|
|
this.submit = currentPage.getHtmlElementById("submit");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public <T> T createMessage(String summary, String text) throws Exception {
|
|
|
|
|
setSummary(summary);
|
|
|
|
|
|
|
|
|
|
HtmlPage result = submit.click();
|
|
|
|
|
boolean error = CreateMessagePage.at(result);
|
|
|
|
|
|
|
|
|
|
return (T) (error ? new CreateMessagePage(result) : new ViewMessagePage(result));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setSummary(String summary) throws Exception {
|
|
|
|
|
summaryInput.setValueAttribute(summary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean at(HtmlPage page) {
|
|
|
|
|
return "Create Message".equals(page.getTitleText());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Formerly, this pattern is known as the
|
2017-04-19 01:36:46 +08:00
|
|
|
|
https://github.com/SeleniumHQ/selenium/wiki/PageObjects[Page Object Pattern]. While we can
|
2015-06-24 00:31:48 +08:00
|
|
|
|
certainly do this with HtmlUnit, WebDriver provides some tools that we will explore in the
|
2015-07-29 00:46:18 +08:00
|
|
|
|
following sections to make this pattern much easier to implement.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-webdriver-setup]]
|
|
|
|
|
====== MockMvc and WebDriver Setup
|
|
|
|
|
|
2015-09-01 06:53:00 +08:00
|
|
|
|
To use Selenium WebDriver with the Spring MVC Test framework, make sure that your project
|
|
|
|
|
includes a test dependency on `org.seleniumhq.selenium:selenium-htmlunit-driver`.
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We can easily create a Selenium `WebDriver` that integrates with `MockMvc` using the
|
|
|
|
|
`MockMvcHtmlUnitDriverBuilder` as follows.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
@Autowired
|
|
|
|
|
WebApplicationContext context;
|
|
|
|
|
|
|
|
|
|
WebDriver driver;
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
|
|
|
|
driver = MockMvcHtmlUnitDriverBuilder
|
|
|
|
|
.webAppContextSetup(context)
|
2015-07-28 04:41:22 +08:00
|
|
|
|
.build();
|
2015-06-24 00:31:48 +08:00
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
|
|
|
|
This is a simple example of using `MockMvcHtmlUnitDriverBuilder`.
|
|
|
|
|
For more advanced usage, refer to <<Advanced MockMvcHtmlUnitDriverBuilder>>
|
|
|
|
|
====
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
This will ensure that any URL referencing `localhost` as the server will be directed to
|
|
|
|
|
our `MockMvc` instance without the need for a real HTTP connection. Any other URL will be
|
|
|
|
|
requested using a network connection as normal. This allows us to easily test the use of
|
|
|
|
|
CDNs.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-webdriver-usage]]
|
|
|
|
|
====== MockMvc and WebDriver Usage
|
|
|
|
|
|
|
|
|
|
Now we can use WebDriver as we normally would, but without the need to deploy our
|
2015-07-29 00:46:18 +08:00
|
|
|
|
application to a Servlet container. For example, we can request the view to create
|
|
|
|
|
a message with the following.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
CreateMessagePage page = CreateMessagePage.to(driver);
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
We can then fill out the form and submit it to create a message.
|
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
ViewMessagePage viewMessagePage =
|
|
|
|
|
page.createMessage(ViewMessagePage.class, expectedSummary, expectedText);
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
This improves on the design of our
|
2015-07-29 00:46:18 +08:00
|
|
|
|
<<spring-mvc-test-server-htmlunit-mah-usage,HtmlUnit test>> by leveraging the _Page Object
|
|
|
|
|
Pattern_. As we mentioned in <<spring-mvc-test-server-htmlunit-webdriver-why>>, we can
|
|
|
|
|
use the Page Object Pattern with HtmlUnit, but it is much easier with WebDriver. Let's
|
|
|
|
|
take a look at our new `CreateMessagePage` implementation.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
public class CreateMessagePage
|
|
|
|
|
extends AbstractPage { // <1>
|
|
|
|
|
|
|
|
|
|
// <2>
|
|
|
|
|
private WebElement summary;
|
|
|
|
|
private WebElement text;
|
|
|
|
|
|
|
|
|
|
// <3>
|
|
|
|
|
@FindBy(css = "input[type=submit]")
|
|
|
|
|
private WebElement submit;
|
|
|
|
|
|
|
|
|
|
public CreateMessagePage(WebDriver driver) {
|
|
|
|
|
super(driver);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public <T> T createMessage(Class<T> resultPage, String summary, String details) {
|
|
|
|
|
this.summary.sendKeys(summary);
|
|
|
|
|
this.text.sendKeys(details);
|
|
|
|
|
this.submit.click();
|
|
|
|
|
return PageFactory.initElements(driver, resultPage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static CreateMessagePage to(WebDriver driver) {
|
|
|
|
|
driver.get("http://localhost:9990/mail/messages/form");
|
|
|
|
|
return PageFactory.initElements(driver, CreateMessagePage.class);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
<1> The first thing you will notice is that `CreateMessagePage` extends the
|
|
|
|
|
`AbstractPage`. We won't 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, etc., this logic can be placed in a shared
|
2015-06-24 00:31:48 +08:00
|
|
|
|
location.
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
<2> The next thing you will notice is that we have a member variable for each of the
|
|
|
|
|
parts of the HTML page that we are interested in. These are of type `WebElement`.
|
2017-04-19 01:36:46 +08:00
|
|
|
|
``WebDriver``'s https://github.com/SeleniumHQ/selenium/wiki/PageFactory[PageFactory] allows
|
2015-07-29 00:46:18 +08:00
|
|
|
|
us to remove a lot of code from the HtmlUnit version of `CreateMessagePage` by
|
|
|
|
|
automatically resolving each `WebElement`. The
|
2017-04-19 01:36:46 +08:00
|
|
|
|
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[PageFactory#initElements(WebDriver,Class<T>)]
|
2015-07-29 00:46:18 +08:00
|
|
|
|
method will automatically resolve each `WebElement` by using the field name and looking it
|
|
|
|
|
up by the `id` or `name` of the element within the HTML page.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
<3> We can use the
|
2017-04-19 01:36:46 +08:00
|
|
|
|
https://github.com/SeleniumHQ/selenium/wiki/PageFactory#making-the-example-work-using-annotations[@FindBy annotation]
|
2015-07-29 00:46:18 +08:00
|
|
|
|
to override the default lookup behavior. Our example demonstrates how to use the `@FindBy`
|
|
|
|
|
annotation to look up our submit button using a css selector, *input[type=submit]*.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
Finally, we can verify that a new message was created successfully. The following
|
|
|
|
|
assertions use the https://code.google.com/p/fest/[FEST assertion library].
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
|
|
|
|
|
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We can see that our `ViewMessagePage` allows us to interact with our custom domain
|
2015-06-24 00:31:48 +08:00
|
|
|
|
model. For example, it exposes a method that returns a `Message` object.
|
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
public Message getMessage() throws ParseException {
|
|
|
|
|
Message message = new Message();
|
|
|
|
|
message.setId(getId());
|
|
|
|
|
message.setCreated(getCreated());
|
|
|
|
|
message.setSummary(getSummary());
|
|
|
|
|
message.setText(getText());
|
|
|
|
|
return message;
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
We can then leverage the rich domain objects in our assertions.
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
Lastly, don't forget to _close_ the `WebDriver` instance when the test is complete.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
@After
|
|
|
|
|
public void destroy() {
|
2015-07-28 04:32:37 +08:00
|
|
|
|
if (driver != null) {
|
2015-06-24 00:31:48 +08:00
|
|
|
|
driver.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
For additional information on using WebDriver, refer to the Selenium
|
2017-04-19 01:36:46 +08:00
|
|
|
|
https://github.com/SeleniumHQ/selenium/wiki/Getting-Started[WebDriver documentation].
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-webdriver-advanced-builder]]
|
|
|
|
|
====== Advanced MockMvcHtmlUnitDriverBuilder
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
In the examples so far, we have used `MockMvcHtmlUnitDriverBuilder` in the simplest way
|
|
|
|
|
possible, by building a `WebDriver` based on the `WebApplicationContext` loaded for us by
|
|
|
|
|
the Spring TestContext Framework. This approach is repeated here.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
2015-07-28 04:32:37 +08:00
|
|
|
|
@Autowired
|
|
|
|
|
WebApplicationContext context;
|
|
|
|
|
|
|
|
|
|
WebDriver driver;
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
2015-07-28 04:32:37 +08:00
|
|
|
|
driver = MockMvcHtmlUnitDriverBuilder
|
2015-06-24 00:31:48 +08:00
|
|
|
|
.webAppContextSetup(context)
|
2015-07-28 01:27:15 +08:00
|
|
|
|
.build();
|
2015-06-24 00:31:48 +08:00
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We can also specify additional configuration options.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
2015-07-28 04:32:37 +08:00
|
|
|
|
WebDriver driver;
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
2015-07-28 04:32:37 +08:00
|
|
|
|
driver = MockMvcHtmlUnitDriverBuilder
|
2015-06-24 00:31:48 +08:00
|
|
|
|
// demonstrates applying a MockMvcConfigurer (Spring Security)
|
|
|
|
|
.webAppContextSetup(context, springSecurity())
|
|
|
|
|
// for illustration only - defaults to ""
|
|
|
|
|
.contextPath("")
|
2015-07-28 04:32:37 +08:00
|
|
|
|
// By default MockMvc is used for localhost only;
|
2015-07-29 00:46:18 +08:00
|
|
|
|
// the following will use MockMvc for example.com and example.org as well
|
2015-06-24 00:31:48 +08:00
|
|
|
|
.useMockMvcForHosts("example.com","example.org")
|
2015-07-28 01:27:15 +08:00
|
|
|
|
.build();
|
2015-06-24 00:31:48 +08:00
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
As an alternative, we can perform the exact same setup by configuring the `MockMvc`
|
|
|
|
|
instance separately and supplying it to the `MockMvcHtmlUnitDriverBuilder` as follows.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
MockMvc mockMvc = MockMvcBuilders
|
|
|
|
|
.webAppContextSetup(context)
|
|
|
|
|
.apply(springSecurity())
|
|
|
|
|
.build();
|
|
|
|
|
|
2015-07-28 04:32:37 +08:00
|
|
|
|
driver = MockMvcHtmlUnitDriverBuilder
|
2015-06-24 00:31:48 +08:00
|
|
|
|
.mockMvcSetup(mockMvc)
|
|
|
|
|
// for illustration only - defaults to ""
|
|
|
|
|
.contextPath("")
|
2015-07-28 04:32:37 +08:00
|
|
|
|
// By default MockMvc is used for localhost only;
|
2015-07-29 00:46:18 +08:00
|
|
|
|
// the following will use MockMvc for example.com and example.org as well
|
2015-06-24 00:31:48 +08:00
|
|
|
|
.useMockMvcForHosts("example.com","example.org")
|
2015-07-28 01:27:15 +08:00
|
|
|
|
.build();
|
2015-06-24 00:31:48 +08:00
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
This is more verbose, but by building the `WebDriver` with a `MockMvc` instance we have
|
2015-07-28 04:32:37 +08:00
|
|
|
|
the full power of `MockMvc` at our fingertips.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[TIP]
|
|
|
|
|
====
|
|
|
|
|
For additional information on creating a `MockMvc` instance refer to
|
|
|
|
|
<<spring-mvc-test-server-setup-options>>.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-geb]]
|
|
|
|
|
===== MockMvc and Geb
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
In the previous section, we saw how to use `MockMvc` with `WebDriver`. In this section,
|
|
|
|
|
we will use http://www.gebish.org/[Geb] to make our tests even Groovy-er.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-geb-why]]
|
|
|
|
|
====== Why Geb and MockMvc?
|
|
|
|
|
|
|
|
|
|
Geb is backed by WebDriver, so it offers many of the
|
2015-07-29 00:46:18 +08:00
|
|
|
|
<<spring-mvc-test-server-htmlunit-webdriver-why,same benefits>> that we get from
|
|
|
|
|
WebDriver. However, Geb makes things even easier by taking care of some of the
|
|
|
|
|
boilerplate code for us.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-geb-setup]]
|
|
|
|
|
====== MockMvc and Geb Setup
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We can easily initialize a Geb `Browser` with a Selenium `WebDriver` that uses `MockMvc`
|
|
|
|
|
as follows.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,groovy]
|
|
|
|
|
----
|
|
|
|
|
def setup() {
|
|
|
|
|
browser.driver = MockMvcHtmlUnitDriverBuilder
|
2015-07-29 00:46:18 +08:00
|
|
|
|
.webAppContextSetup(context)
|
2015-07-28 04:41:22 +08:00
|
|
|
|
.build()
|
2015-06-24 00:31:48 +08:00
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
|
|
|
|
This is a simple example of using `MockMvcHtmlUnitDriverBuilder`.
|
|
|
|
|
For more advanced usage, refer to <<Advanced MockMvcHtmlUnitDriverBuilder>>
|
|
|
|
|
====
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
This will ensure that any URL referencing `localhost` as the server will be directed to
|
|
|
|
|
our `MockMvc` instance without the need for a real HTTP connection. Any other URL will be
|
|
|
|
|
requested using a network connection as normal. This allows us to easily test the use of
|
|
|
|
|
CDNs.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-server-htmlunit-geb-usage]]
|
|
|
|
|
====== MockMvc and Geb Usage
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
Now we can use Geb 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:
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,groovy]
|
|
|
|
|
----
|
|
|
|
|
to CreateMessagePage
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
We can then fill out the form and submit it to create a message.
|
|
|
|
|
|
|
|
|
|
[source,groovy]
|
|
|
|
|
----
|
|
|
|
|
when:
|
|
|
|
|
form.summary = expectedSummary
|
|
|
|
|
form.text = expectedMessage
|
|
|
|
|
submit.click(ViewMessagePage)
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Any unrecognized method calls or property accesses/references that are not found will be
|
|
|
|
|
forwarded to the current page object. This removes a lot of the boilerplate code we needed
|
|
|
|
|
when using WebDriver directly.
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
As with direct WebDriver usage, this improves on the design of our
|
|
|
|
|
<<spring-mvc-test-server-htmlunit-mah-usage,HtmlUnit test>> by leveraging the _Page Object
|
|
|
|
|
Pattern_. As mentioned previously, we can use the Page Object Pattern with HtmlUnit and
|
|
|
|
|
WebDriver, but it is even easier with Geb. Let's take a look at our new Groovy-based
|
|
|
|
|
`CreateMessagePage` implementation.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,groovy]
|
|
|
|
|
----
|
|
|
|
|
class CreateMessagePage extends Page {
|
|
|
|
|
static url = 'messages/form'
|
2015-07-29 00:46:18 +08:00
|
|
|
|
static at = { assert title == 'Messages : Create'; true }
|
2015-06-24 00:31:48 +08:00
|
|
|
|
static content = {
|
|
|
|
|
submit { $('input[type=submit]') }
|
|
|
|
|
form { $('form') }
|
|
|
|
|
errors(required:false) { $('label.error, .alert-error')?.text() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
The first thing you will notice is that our `CreateMessagePage` extends `Page`. We won't
|
|
|
|
|
go over the details of `Page`, but in summary it contains common functionality for all of
|
|
|
|
|
our pages. The next thing you will notice is that we define a URL in which this page can
|
|
|
|
|
be found. This allows us to navigate to the page as follows.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,groovy]
|
|
|
|
|
----
|
|
|
|
|
to CreateMessagePage
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We also have an `at` closure that determines if we are at the specified page. It should return
|
|
|
|
|
`true` if we are on the correct page. This is why we can assert that we are on the correct
|
|
|
|
|
page as follows.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,groovy]
|
|
|
|
|
----
|
|
|
|
|
then:
|
|
|
|
|
at CreateMessagePage
|
|
|
|
|
errors.contains('This field is required.')
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
2015-07-29 00:46:18 +08:00
|
|
|
|
We use an assertion in the closure, so that we can determine where things went wrong if
|
|
|
|
|
we were at the wrong page.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
====
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
Next we create a `content` closure that specifies all the areas of interest within the page.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
We can use a
|
2017-04-19 01:36:46 +08:00
|
|
|
|
http://www.gebish.org/manual/current/#the-jquery-ish-navigator-api[jQuery-ish Navigator API]
|
2015-06-24 00:31:48 +08:00
|
|
|
|
to select the content we are interested in.
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
Finally, we can verify that a new message was created successfully.
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
|
|
|
|
[source,groovy]
|
|
|
|
|
----
|
|
|
|
|
then:
|
|
|
|
|
at ViewMessagePage
|
|
|
|
|
success == 'Successfully created a new message'
|
|
|
|
|
id
|
|
|
|
|
date
|
|
|
|
|
summary == expectedSummary
|
|
|
|
|
message == expectedMessage
|
|
|
|
|
----
|
|
|
|
|
|
2015-07-29 00:46:18 +08:00
|
|
|
|
For further details on how to get the most out of Geb, consult
|
|
|
|
|
http://www.gebish.org/manual/current/[The Book of Geb] user's manual.
|
|
|
|
|
|
2015-06-24 00:31:48 +08:00
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
[[spring-mvc-test-client]]
|
|
|
|
|
==== Client-Side REST Tests
|
2016-01-15 04:15:07 +08:00
|
|
|
|
Client-side tests can be used to test code that internally uses the `RestTemplate`.
|
|
|
|
|
The idea is to declare expected requests and to provide "stub" responses so that
|
|
|
|
|
you can focus on testing the code in isolation, i.e. without running a server.
|
|
|
|
|
Here is an example:
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
|
|
|
|
2016-02-25 04:23:30 +08:00
|
|
|
|
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
|
|
|
|
|
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess());
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
2016-01-15 04:15:07 +08:00
|
|
|
|
// Test code that uses the above RestTemplate ...
|
2015-03-03 18:38:01 +08:00
|
|
|
|
|
|
|
|
|
mockServer.verify();
|
|
|
|
|
----
|
|
|
|
|
|
2016-02-25 04:23:30 +08:00
|
|
|
|
In the above example, `MockRestServiceServer`, the central class for client-side REST
|
|
|
|
|
tests, configures the `RestTemplate` with a custom `ClientHttpRequestFactory` that
|
2015-03-03 18:38:01 +08:00
|
|
|
|
asserts actual requests against expectations and returns "stub" responses. In this case
|
2016-02-25 04:23:30 +08:00
|
|
|
|
we expect a request to "/greeting" and want to return a 200 response with
|
|
|
|
|
"text/plain" content. We could define as additional expected requests and stub responses as
|
|
|
|
|
needed. When expected requests and stub responses are defined, the `RestTemplate` can be
|
|
|
|
|
used in client-side code as usual. At the end of testing `mockServer.verify()` can be
|
|
|
|
|
used to verify that all expectations have been satisfied.
|
|
|
|
|
|
|
|
|
|
By default requests are expected in the order in which expectations were declared.
|
|
|
|
|
You can set the `ignoreExpectOrder` option when building the server in which case
|
|
|
|
|
all expectations are checked (in order) to find a match for a given request. That
|
|
|
|
|
means requests are allowed to come in any order. Here is an example:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
2016-05-26 21:48:16 +08:00
|
|
|
|
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
|
2016-02-25 04:23:30 +08:00
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Even with unordered requests by default each request is allowed to execute once only.
|
|
|
|
|
The `expect` method provides an overloaded variant that accepts an `ExpectedCount`
|
|
|
|
|
argument that specifies a count range, e.g. `once`, `manyTimes`, `max`, `min`,
|
|
|
|
|
`between`, and so on. Here is an example:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
|
|
|
|
|
|
|
|
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
|
|
|
|
|
mockServer.expect(times(2), requestTo("/foo")).andRespond(withSuccess());
|
|
|
|
|
mockServer.expect(times(3), requestTo("/bar")).andRespond(withSuccess());
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
mockServer.verify();
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Note that when `ignoreExpectOrder` is not set (the default), and therefore requests
|
|
|
|
|
are expected in order of declaration, then that order only applies to the first of
|
|
|
|
|
any expected request. For example if "/foo" is expected 2 times followed by "/bar"
|
|
|
|
|
3 times, then there should be a request to "/foo" before there is a request to "/bar"
|
|
|
|
|
but aside from that subsequent "/foo" and "/bar" requests can come at any time.
|
|
|
|
|
|
|
|
|
|
As an alternative to all of the above the client-side test support also provides a
|
|
|
|
|
`ClientHttpRequestFactory` implementation that can be configured into a `RestTemplate`
|
|
|
|
|
to bind it to a `MockMvc` instance. That allows processing requests using actual
|
|
|
|
|
server-side logic but without running a server. Here is an example:
|
2016-01-15 04:15:07 +08:00
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
|
|
|
|
this.restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));
|
|
|
|
|
|
|
|
|
|
// Test code that uses the above RestTemplate ...
|
|
|
|
|
|
|
|
|
|
mockServer.verify();
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-03-03 18:38:01 +08:00
|
|
|
|
[[spring-mvc-test-client-static-imports]]
|
|
|
|
|
===== Static Imports
|
|
|
|
|
Just like with server-side tests, the fluent API for client-side tests requires a few
|
|
|
|
|
static imports. Those are easy to find by searching __"MockRest*"__. Eclipse users
|
|
|
|
|
should add `"MockRestRequestMatchers.{asterisk}"` and `"MockRestResponseCreators.{asterisk}"`
|
2016-02-25 04:23:30 +08:00
|
|
|
|
as "favorite static members" in the Eclipse preferences under
|
2015-03-03 18:38:01 +08:00
|
|
|
|
__Java -> Editor -> Content Assist -> Favorites__.
|
|
|
|
|
That allows using content assist after typing the first character of the
|
|
|
|
|
static method name. Other IDEs (e.g. IntelliJ) may not require any additional
|
|
|
|
|
configuration. Just check the support for code completion on static members.
|
|
|
|
|
|
|
|
|
|
[[spring-mvc-test-client-resources]]
|
|
|
|
|
===== Further Examples of Client-side REST Tests
|
|
|
|
|
Spring MVC Test's own tests include
|
|
|
|
|
https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/client/samples[example
|
|
|
|
|
tests] of client-side REST tests.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testing-examples-petclinic]]
|
|
|
|
|
=== PetClinic Example
|
|
|
|
|
|
|
|
|
|
The PetClinic application, available on
|
|
|
|
|
https://github.com/spring-projects/spring-petclinic[GitHub], illustrates several features
|
2016-05-04 00:45:47 +08:00
|
|
|
|
of the __Spring TestContext Framework__ in a JUnit 4 environment. Most test functionality
|
2015-03-03 18:38:01 +08:00
|
|
|
|
is included in the `AbstractClinicTests`, for which a partial listing is shown below:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
|
|
|
// import ...
|
|
|
|
|
|
|
|
|
|
**@ContextConfiguration**
|
|
|
|
|
public abstract class AbstractClinicTests **extends AbstractTransactionalJUnit4SpringContextTests** {
|
|
|
|
|
|
|
|
|
|
**@Autowired**
|
|
|
|
|
protected Clinic clinic;
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void getVets() {
|
|
|
|
|
Collection<Vet> vets = this.clinic.getVets();
|
|
|
|
|
assertEquals("JDBC query must show the same number of vets",
|
|
|
|
|
**super.countRowsInTable("VETS")**, vets.size());
|
|
|
|
|
Vet v1 = EntityUtils.getById(vets, Vet.class, 2);
|
|
|
|
|
assertEquals("Leary", v1.getLastName());
|
|
|
|
|
assertEquals(1, v1.getNrOfSpecialties());
|
|
|
|
|
assertEquals("radiology", (v1.getSpecialties().get(0)).getName());
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
|
|
|
|
* This test case extends the `AbstractTransactionalJUnit4SpringContextTests` class, from
|
|
|
|
|
which it inherits configuration for Dependency Injection (through the
|
|
|
|
|
`DependencyInjectionTestExecutionListener`) and transactional behavior (through the
|
|
|
|
|
`TransactionalTestExecutionListener`).
|
|
|
|
|
* The `clinic` instance variable -- the application object being tested -- is set by
|
|
|
|
|
Dependency Injection through `@Autowired` semantics.
|
|
|
|
|
* The `getVets()` method illustrates how you can use the inherited `countRowsInTable()`
|
|
|
|
|
method to easily verify the number of rows in a given table, thus verifying correct
|
|
|
|
|
behavior of the application code being tested. This allows for stronger tests and
|
|
|
|
|
lessens dependency on the exact test data. For example, you can add additional rows in
|
|
|
|
|
the database without breaking tests.
|
|
|
|
|
* Like many integration tests that use a database, most of the tests in
|
|
|
|
|
`AbstractClinicTests` depend on a minimum amount of data already in the database before
|
|
|
|
|
the test cases run. Alternatively, you might choose to populate the database within the
|
|
|
|
|
test fixture set up of your test cases -- again, within the same transaction as the
|
|
|
|
|
tests.
|
|
|
|
|
|
|
|
|
|
The PetClinic application supports three data access technologies: JDBC, Hibernate, and
|
|
|
|
|
JPA. By declaring `@ContextConfiguration` without any specific resource locations, the
|
|
|
|
|
`AbstractClinicTests` class will have its application context loaded from the default
|
|
|
|
|
location, `AbstractClinicTests-context.xml`, which declares a common `DataSource`.
|
|
|
|
|
Subclasses specify additional context locations that must declare a
|
|
|
|
|
`PlatformTransactionManager` and a concrete implementation of `Clinic`.
|
|
|
|
|
|
|
|
|
|
For example, the Hibernate implementation of the PetClinic tests contains the following
|
|
|
|
|
implementation. For this example, `HibernateClinicTests` does not contain a single line
|
|
|
|
|
of code: we only need to declare `@ContextConfiguration`, and the tests are inherited
|
|
|
|
|
from `AbstractClinicTests`. Because `@ContextConfiguration` is declared without any
|
|
|
|
|
specific resource locations, the __Spring TestContext Framework__ loads an application
|
|
|
|
|
context from all the beans defined in `AbstractClinicTests-context.xml` (i.e., the
|
|
|
|
|
inherited locations) and `HibernateClinicTests-context.xml`, with
|
|
|
|
|
`HibernateClinicTests-context.xml` possibly overriding beans defined in
|
|
|
|
|
`AbstractClinicTests-context.xml`.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
|
----
|
|
|
|
|
**@ContextConfiguration**
|
|
|
|
|
public class HibernateClinicTests extends AbstractClinicTests { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
In a large-scale application, the Spring configuration is often split across multiple
|
|
|
|
|
files. Consequently, configuration locations are typically specified in a common base
|
|
|
|
|
class for all application-specific integration tests. Such a base class may also add
|
|
|
|
|
useful instance variables -- populated by Dependency Injection, naturally -- such as a
|
|
|
|
|
`SessionFactory` in the case of an application using Hibernate.
|
|
|
|
|
|
|
|
|
|
As far as possible, you should have exactly the same Spring configuration files in your
|
|
|
|
|
integration tests as in the deployed environment. One likely point of difference
|
|
|
|
|
concerns database connection pooling and transaction infrastructure. If you are
|
|
|
|
|
deploying to a full-blown application server, you will probably use its connection pool
|
|
|
|
|
(available through JNDI) and JTA implementation. Thus in production you will use a
|
|
|
|
|
`JndiObjectFactoryBean` or `<jee:jndi-lookup>` for the `DataSource` and
|
|
|
|
|
`JtaTransactionManager`. JNDI and JTA will not be available in out-of-container
|
|
|
|
|
integration tests, so you should use a combination like the Commons DBCP
|
|
|
|
|
`BasicDataSource` and `DataSourceTransactionManager` or `HibernateTransactionManager`
|
|
|
|
|
for them. You can factor out this variant behavior into a single XML file, having the
|
|
|
|
|
choice between application server and a 'local' configuration separated from all other
|
|
|
|
|
configuration, which will not vary between the test and production environments. In
|
|
|
|
|
addition, it is advisable to use properties files for connection settings. See the
|
|
|
|
|
PetClinic application for an example.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[testing-resources]]
|
|
|
|
|
== Further Resources
|
|
|
|
|
Consult the following resources for more information about testing:
|
|
|
|
|
|
|
|
|
|
* http://www.junit.org/[JUnit]: "__A programmer-oriented testing framework for Java__".
|
|
|
|
|
Used by the Spring Framework in its test suite.
|
|
|
|
|
* http://testng.org/[TestNG]: A testing framework inspired by JUnit with added support
|
|
|
|
|
for annotations, test groups, data-driven testing, distributed testing, etc.
|
2015-10-01 04:06:07 +08:00
|
|
|
|
* http://joel-costigliola.github.io/assertj/[AssertJ]: "__Fluent assertions for Java__"
|
|
|
|
|
including support for Java 8 lambdas, streams, etc.
|
2015-07-20 20:35:33 +08:00
|
|
|
|
* http://en.wikipedia.org/wiki/Mock_Object[Mock Objects]: Article in Wikipedia.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
* http://www.mockobjects.com/[MockObjects.com]: Web site dedicated to mock objects, a
|
|
|
|
|
technique for improving the design of code within test-driven development.
|
2015-07-20 20:35:33 +08:00
|
|
|
|
* http://mockito.org/[Mockito]: Java mock library based on the
|
|
|
|
|
http://xunitpatterns.com/Test%20Spy.html[test spy] pattern.
|
|
|
|
|
* http://www.easymock.org/[EasyMock]: Java library "__that provides Mock Objects for
|
2015-03-03 18:38:01 +08:00
|
|
|
|
interfaces (and objects through the class extension) by generating them on the fly
|
2015-07-20 20:35:33 +08:00
|
|
|
|
using Java's proxy mechanism.__" Used by the Spring Framework in its test suite.
|
2015-03-03 18:38:01 +08:00
|
|
|
|
* http://www.jmock.org/[JMock]: Library that supports test-driven development of Java
|
|
|
|
|
code with mock objects.
|
|
|
|
|
* http://dbunit.sourceforge.net/[DbUnit]: JUnit extension (also usable with Ant and
|
|
|
|
|
Maven) targeted for database-driven projects that, among other things, puts your
|
|
|
|
|
database into a known state between test runs.
|
|
|
|
|
* http://grinder.sourceforge.net/[The Grinder]: Java load testing framework.
|
|
|
|
|
|