Spring Boot provides a number of useful tools for testing your application. The
spring-boot-starter-test
POM provides Spring Test, JUnit, Hamcrest and Mockito
dependencies. There are also useful test utilities in the core spring-boot
module
under the org.springframework.boot.test
package.
If you use the
spring-boot-starter-test
“Starter POM” (in the test
scope
), you will find
the following provided libraries:
assertThat
style JUnit assertions.
These are common libraries that we generally find useful when writing tests. You are free to add additional test dependencies of your own if these don’t suit your needs.
One of the major advantages of dependency injection is that it should make your code
easier to unit test. You can simply instantiate objects using the new
operator without
even involving Spring. You can also use mock objects instead of real dependencies.
Often you need to move beyond “unit testing” and start “integration testing” (with
a Spring ApplicationContext
actually involved in the process). It’s useful to be able
to perform integration testing without requiring deployment of your application or
needing to connect to other infrastructure.
The Spring Framework includes a dedicated test module for just such integration testing.
You can declare a dependency directly to org.springframework:spring-test
or use the
spring-boot-starter-test
“Starter POM” to pull it in transitively.
If you have not used the spring-test
module before you should start by reading the
relevant section of the Spring Framework reference
documentation.
A Spring Boot application is just a Spring ApplicationContext
so nothing very special
has to be done to test it beyond what you would normally do with a vanilla Spring context.
One thing to watch out for though is that the external properties, logging and other
features of Spring Boot are only installed in the context by default if you use
SpringApplication
to create it.
Spring Boot provides a @SpringApplicationConfiguration
annotation as an alternative
to the standard spring-test
@ContextConfiguration
annotation. If you use
@SpringApplicationConfiguration
to configure the ApplicationContext
used in your
tests, it will be created via SpringApplication
and you will get the additional Spring
Boot features.
For example:
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = SampleDataJpaApplication.class) public class CityRepositoryIntegrationTests { @Autowired CityRepository repository; // ... }
Tip | |
---|---|
The context loader guesses whether you want to test a web application or not (e.g.
with |
If you want a web application to start up and listen on its normal port, so you can test
it with HTTP (e.g. using RestTemplate
), annotate your test class (or one of its
superclasses) with @IntegrationTest
. This can be very useful because it means you can
test the full stack of your application, but also inject its components into the test
class and use them to assert the internal state of the application after an HTTP
interaction. For Example:
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = SampleDataJpaApplication.class) @WebAppConfiguration @IntegrationTest public class CityRepositoryIntegrationTests { @Autowired CityRepository repository; RestTemplate restTemplate = new TestRestTemplate(); // ... interact with the running server }
Note | |
---|---|
Spring’s test framework will cache application contexts between tests. Therefore, as long as your tests share the same configuration, the time consuming process of starting and stopping the server will only happen once, regardless of the number of tests that actually run. |
To change the port you can add environment properties to @IntegrationTest
as colon- or
equals-separated name-value pairs, e.g. @IntegrationTest("server.port:9000")
.
Additionally you can set the server.port
and management.port
properties to 0
in order to run your integration tests using random ports. For example:
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MyApplication.class) @WebAppConfiguration @IntegrationTest({"server.port=0", "management.port=0"}) public class SomeIntegrationTests { // ... }
See Section 55.4, “Discover the HTTP port at runtime” for a description of how you can discover the actual port that was allocated for the duration of the tests.
A few test utility classes are packaged as part of spring-boot
that are generally
useful when testing your application.
ConfigFileApplicationContextInitializer
is an ApplicationContextInitializer
that
can apply to your tests to load Spring Boot application.properties
files. You can use
this when you don’t need the full features provided by @SpringApplicationConfiguration
.
@ContextConfiguration(classes = Config.class, initializers = ConfigFileApplicationContextInitializer.class)
EnvironmentTestUtils
allows you to quickly add properties to a
ConfigurableEnvironment
or ConfigurableApplicationContext
. Simply call it with
key=value
strings:
EnvironmentTestUtils.addEnvironment(env, "org=Spring", "name=Boot");
OutputCapture
is a JUnit Rule
that you can use to capture System.out
and
System.err
output. Simply declare the capture as a @Rule
then use toString()
for assertions:
import org.junit.Rule; import org.junit.Test; import org.springframework.boot.test.OutputCapture; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; public class MyTest { @Rule public OutputCapture capture = new OutputCapture(); @Test public void testName() throws Exception { System.out.println("Hello World!"); assertThat(capture.toString(), containsString("World")); } }
TestRestTemplate
is a convenience subclass of Spring’s RestTemplate
that is
useful in integration tests. You can get a vanilla template or one that sends Basic HTTP
authentication (with a username and password). In either case the template will behave
in a test-friendly way: not following redirects (so you can assert the response
location), ignoring cookies (so the template is stateless), and not throwing exceptions
on server-side errors. It is recommended, but not mandatory, to use Apache HTTP Client
(version 4.3.2 or better), and if you have that on your classpath the TestRestTemplate
will respond by configuring the client appropriately.
public class MyTest { RestTemplate template = new TestRestTemplate(); @Test public void testRequest() throws Exception { HttpHeaders headers = template.getForEntity("http://myhost.com", String.class).getHeaders(); assertThat(headers.getLocation().toString(), containsString("myotherhost")); } }