Integration Testing a Spring Boot Application

Spring Boot brings about some welcome defaults configurations that significantly decreases the development time of Spring projects. It also has some useful additions when it comes to simplified integration testing. Traditionally, one would use the build script to fire up an embedded container such as Jetty, Tomcat or Cargo, but since a Spring Boot web application already comprises an embedded servlet container some convenient utilities have been created so it can be reused for integration testing.

Sample Application

Let us have a look at a sample application. First, there is an entity class based on JPA:

@Entity
public class Character {

    @Column(name = "name")
    private String name;

    @Id
    @Column(name = "id")
    @GeneratedValue
    private Integer id;

    // getters, setters, etc, omitted
}

A corresponding repository based on Spring Data JPA:

public interface CharacterRepository extends JpaRepository {
}

A Spring 4 Controller:

@RestController
class CharacterController {

    @Autowired
    private CharacterRepository repository;

    @RequestMapping("/characters")
    List characters() {
        return repository.findAll();
    }

    @RequestMapping(value = "/characters/{id}", method = RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    void delete(@PathVariable("id") Integer id) {
        repository.delete(id);
    }

    // more controller methods
}

And a Spring Boot application:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Unless you have used Spring Boot before, you will be pleasantly surprised that (besides adding the appropriate dependencies on your classpath) this is actually all that is required to create a complete Spring Boot web application including database persistence! If, let’s say that Mickey Mouse, Minnie Mouse and Pluto were the only three characters in the database, a request to /characters would give the following response:

$ curl localhost:8080/characters
[{"name":"Mickey Mouse","id":1},{"name":"Minnie Mouse","id":2},{"name":"Pluto","id":3}]

Curl is a good tool for trying out things on the command line, but it is less suitable for writing automated integration tests.

Spring Boot Test

To verify that the application works, one could create the following test class. Scroll down to read explanations for each // nbr below.

@RunWith(SpringJUnit4ClassRunner.class)   // 1
@SpringApplicationConfiguration(classes = Application.class)   // 2
@WebAppConfiguration   // 3
@IntegrationTest("server.port:0")   // 4
public class CharacterControllerTest {

    @Autowired   // 5
    CharacterRepository repository;

    Character mickey;
    Character minnie;
    Character pluto;

    @Value("${local.server.port}")   // 6
    int port;

    @Before
    public void setUp() {
        // 7
        mickey = new Character("Mickey Mouse");
        minnie = new Character("Minnie Mouse");
        pluto = new Character("Pluto");

        // 8
        repository.deleteAll();
        repository.save(Arrays.asList(mickey, minnie, pluto));

        // 9
        RestAssured.port = port;
    }

    // 10
    @Test
    public void canFetchMickey() {
        Integer mickeyId = mickey.getId();

        when().
                get("/characters/{id}", mickeyId).
        then().
                statusCode(HttpStatus.SC_OK).
                body("name", Matchers.is("Mickey Mouse")).
                body("id", Matchers.is(mickeyId));
    }

    @Test
    public void canFetchAll() {
        when().
                get("/characters").
        then().
                statusCode(HttpStatus.SC_OK).
                body("name", Matchers.hasItems("Mickey Mouse", "Minnie Mouse", "Pluto"));
    }

    @Test
    public void canDeletePluto() {
        Integer plutoId = pluto.getId();

        when()
                .delete("/characters/{id}", plutoId).
        then().
                statusCode(HttpStatus.SC_NO_CONTENT);
    }
}

Explanation

Hint, more information can be found in the Javadoc of each class, just click on the corresponding link.

  • 1. Like any other Spring based test, we need the SpringJUnit4ClassRunner so that an application context is created.
  • 2. The @SpringApplicationConfiguration annotation is similar to the @ContextConfiguration annotation in that it is used to specify which application context(s) that should be used in the test. Additionally, it will trigger logic for reading Spring Boot specific configurations, properties, and so on.
  • 3. @WebAppConfiguration must be present in order to tell Spring that a WebApplicationContext should be loaded for the test. It also provides an attribute for specifying the path to the root of the web application.
  • 4. @IntegrationTest is used to instruct Spring Boot that the embedded web server should be started. By providing colon- or equals-separated name-value pair(s), any environment variable can be overridden. In this example, the "server.port:0" will override the server’s default port setting. Normally, the server would start using the specified port number, but the value 0 has a special meaning. When specified, it tells Spring Boot to scan the ports on the host environment and start the server on a random, available port. That is useful if you have different services occupying different ports on the development machines and the build server that could potentially collide with the application port, in which case the application will not start. Secondly, if you create multiple integration tests with different application contexts, they may also collide if the tests are running concurrently.
  • 5. We have access to the application context and can use autowiring to inject any Spring bean.
  • 6. The @Value("${local.server.port}”) will be resolved to the actual port number that is used.
  • 7. We create some entities that we can use for validation.
  • 8. The database is cleared and re-initialized for each test so that we always validate against a known state. Since the order of the tests is not defined, chances are that the canFetchAll() test fails if it is executed after the canDeletePluto() test.
  • 9. We instruct Rest Assured to use the correct port. It is an open source project that provides a Java DSL for testing restful services, founded by my colleague Johan Haleby (follow the link to read his blog posts, including more about Rest Assured).
  • 10. Test are implemented by using Rest Assured. You could implement the tests using the TestRestTemplate or any other http client, but I find the Rest Assured syntax both clear and concise.

Advantages

Some advantages with using Spring Boot’s embedded container during testing:

  • Development test turnaround time. You can change a test (or the code that is verified by the test) and execute the test immediately. Compare that with rebuilding the entire project, launch an embedded server, and then execute the test.
  • Spring Boot enable us to create tests that access any Spring bean, the REST API as well as the database directly. If you create tests using an embedded server in your build script you can only interact with the REST API and the database (unless it is an embedded database).
  • The very same container is used during development, testing and production. Hence there is no discrepancy between different vendor implementations or server versions. Moreover, the server configuration will be tested and verified in all environments (typically, you will only change environment specific settings such as database url and password, administrator credentials, etc).

Considerations

  • You cannot create transaction based tests when making requests to the REST API, despite that you could autowire the transaction manager in your test. The reason is that any transaction defined in your application, regardless of where it is declared, will be committed before the server response is sent. Consequently, it will be too late for a test to roll back any state using the @Transactional annotation when the test completes.
  • It is possible to verify your application responses without starting a server by using the Spring Mvc Test Framework framework. Just remove the @IntegrationTest annotation, and rewrite the test implementations using the MockMvc class as showed in this example. The execution time of these tests will be shorter, since no application server is involved.

Spring Boot reference documentation

Dependencies

  • spring-boot-starter-web 1.1.3.RELEASE
  • spring-boot-starter-data-jpa 1.1.3.RELEASE
  • hsqldb 2.3.2
  • spring-boot-starter-test 1.1.3.RELEASE
  • hamcrest-core 1.3
  • rest-assured 2.3.2.

Mattias Severson

Mattias is a senior software engineer specialized in backend architecture and development with experience of cloud based applications and scalable solutions. He is a clean code proponent who appreciates Agile methodologies and pragmatic Test Driven Development. Mattias has experience from many different environments, including everything between big international projects that last for years and solo, single day jobs. He is open-minded and curious about new technologies. Mattias believes in continuous improvement on a personal level as well as in the projects that he is working on. Additionally, Mattias is a frequent speaker at user groups, companies and conferences.

This Post Has 80 Comments

  1. Tony

    This might be a silly question but where do the when().x and then().x method calls in the Controller tests come from (I assume from a static import but from which package)?

    1. Mattias Severson

      @Tony: Both methods are part of Rest Assured, specifically the when() method is a static method of the RestAssured class and the then() is a method in declared in the Response interface. If you are interested in finding out more about Rest Assured, I suggest that you take a look at the usage guide.

  2. Ben Gardella

    You forgot to mention that the spring boot needs to be launched explicitly:

    @BeforeClass
    public static void startBootApp(){
    appContext = SpringApplication.run(MockDataApplication.class, “”);
    }

    @AfterClass
    public static void shutdownBootApp(){
    appContext.close();
    }

    1. Mattias Severson

      @Ben: Thank you for your comment. Actually, that is not necessary, the code that is written in the example above is all that is required to launch the Spring Boot application and execute the test. If you read the Testing Spring Boot applications chapter in the reference docs you will find that:

      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.

  3. Steve Wall

    Great write up! One thing we’d like to do is deploy our war to an external server and run the test against that server. Is there a way to specify the server URL, instead of using the embedded server? For instance, a command line parameter? I see how to do it with @WebAppConfiguration(“http://external-server”). But that requires a code change to switch from the embedded server. Could it be done by using a command line property?

    1. Mattias Severson

      @Steve: Thanks!

      By using an external server instead of the embedded server, you cannot create a Spring integration test (i.e. you cannot use the annotations on the class level of the test above). The external server will create its own application context and instantiate its own Spring beans when your app is started that are inaccessible from the test class. Consequently, you cannot autowire any dependency from your application into your test.
      That said, you can still benefit from using Rest Assured to verify that the API works as expected. If you only need to verify behavior, you can simply ignore the setup method. On the other hand, if you also need to verify state, then you need to create a custom setup method that connects to the same database instance as the external server application and setup its state before the tests. How you connect to the database is not related to how the application connects to the database. You can use the same application context configuration as the application (but as stated before, that means that you will have two separate instances of your application context, each that has its own repository instance), but you can also use tools such as the JdbcTemplate, a raw MongoClient or similar.
      Next, you can use Rest Assured to issue a request and validate the response against your external server, simply specify the url to the server in the get() method, e.g. when().get("http://some-server.com/some-resource/{id}", someId). See the Rest Assured web page and its usage guide for details.

  4. Uzair

    Hi,

    Its work fine when I run it with eclipse. When I run my test using maven build I got this error

    org.apache.jasper.JasperException: The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this applicatio
    n
    at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:56)
    at org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:278)
    at org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:76)
    at org.apache.jasper.compiler.TagLibraryInfoImpl.generateTldResourcePath(TagLibraryInfoImpl.java:242)
    at org.apache.jasper.compiler.TagLibraryInfoImpl.(TagLibraryInfoImpl.java:124)
    at org.apache.jasper.compiler.Parser.parseTaglibDirective(Parser.java:411)
    at org.apache.jasper.compiler.Parser.parseDirective(Parser.java:469)
    at org.apache.jasper.compiler.Parser.parseElements(Parser.java:1430)
    at org.apache.jasper.compiler.Parser.parse(Parser.java:139)
    at org.apache.jasper.compiler.ParserController.doParse(ParserController.java:227)
    at org.apache.jasper.compiler.ParserController.parse(ParserController.java:100)
    at org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:199)
    at org.apache.jasper.compiler.Compiler.compile(Compiler.java:356)
    at org.apache.jasper.compiler.Compiler.compile(Compiler.java:336)

    1. Mattias Severson

      @Uzair: My guess is that you are developing a service that uses JSP for view rendering. Several answers at Stack Overflow as well as this page suggests that you need to add the jstl dependency since it is not included by default by Spring.

      If you are using Spring Boot’s spring-boot-dependencies or spring-boot-starter-parent you can omit the version number (since it is defined in spring-boot-starter-parent). Simply add just the jstl to your dependencies:

      <dependency>
          <groupId>jstl</groupId>
          <artifactId>jstl</artifactId>
      </dependency>
      

      Otherwise, at current date, you also need to add 1.2 as version number.

  5. Lrkwz

    I would suggest to use @WebIntegrationTest inplace of @IntegrationTest.
    I needed it to resolve an exception using HikariCP instead of tomcat-jdbc connection pool.
    Running tests HikariCP caused the error:

    HikariCP Codahale shim says: Codahale metrics library is required but was not found in the classpath

    Using @WebIntegrationTest loaded the correct dependencies.

    1. Mattias Severson

      @Lrkwz: Thank you for your comment. I would also advice that you use the @WebIntegrationTest annotation if you can. The reason I did not use it when I wrote the blog post was that it simply did not exist (the blog post is based on Spring Boot 1.1.3, the @WebIntegrationTest annotation was introduced in release 1.2.1). For more information, please read the Testing Spring Boot applications paragraph in the reference docs.

  6. Alex Biddle

    Thanks for this blog post, it was really useful.

    I had some errors starting the Spring ApplicationContext when originally running my integration test, but found out that it was a dependency issue in my POM (I was originally working from scratch without this post). Once I had cleaned up the dependencies, it worked fine.

  7. Alan

    i’m using spring boot , tomcat , jersey for REST. I see lots of examples for integration tests but i don’t see how to inject mocks into autowired endpoints. Is there any support for this? The endpoints access DAO objects which i would like to mock for testing.

    1. Mattias Severson

      @Alan: What you can do is to create a separate mock application context for your test class that contains a mocked Spring bean that will return your DAO objects, add the mock behavior in each test and then you can use Rest Assured as described above. Remember to reset() your mock objects, e.g. in a @Before or @After method (this can be achieved by autowiring the mocked bean in your class). The reason is that the Spring application context (including the mocked bean instance) will be cached during the execution of the entire test suite and you do not want mock behavior from previous tests to affect later tests. Since you are using Jersey, it is also possible that you may find the Jersey Test Framework useful. I do not know its capabilities or limitations since I have not used it, but I can imagine that you instantiate your resource with mocked beans, implement behavior that you would like to test and then use the Jersey Test Framework to validate.

  8. Alan

    can you provide an example of what separate mock application context would look like? I’m new to spring and spring boot !

    1. Mattias Severson

      @Alan:

      Something like this (assuming that you are using Mockito):

      @SpringBootApplication
      public class MockConfiguration extends ResourceConfig {
      
          public MockConfiguration() {
              register(YourEndpont.class)
          }
      
          @Bean
          YourDependency yourDependency() {
              return Mockito.mock(YourDependency.class)
          }
      }
      

      and in your test (assuming you are using Spring Boot, embedded servlet container and Rest Assured as above):

      @SpringApplicationConfiguration(classes = MockConfiguration.class)   // 2
      @WebAppConfiguration   // 3
      @IntegrationTest("server.port:0")   // 4
      public class CharacterControllerTest {
       
          @Autowired   // 5
          YourDependency yourDependencyMock;
      
          @Value("${local.server.port}")   // 6
          int port;
      
          @Before
          public void setUp() {
              Mockito.reset(yourDependencyMock);
              // 9
              RestAssured.port = port;
          }
      
          // 10
          @Test
          public void testSomething() {
              Mockito.when(yourDependencyMock.getSomeDao())
                  .thenReturn(new SomeDao("someAttribute"));
              
              when().
                      get("/url-to-get-some-dao").
              then().
                      statusCode(HttpStatus.SC_OK).
                      body("attribute", Matchers.is("someAttribute"));
          }
      }
      

      Read more about Mockito in its documentation.

      1. Alan

        I appreciate your help! I’m getting errors : org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.

        running embedded tomcat. It seems that the test config is interfering with other config… Will continue on…. Thx

        1. Mattias Severson

          @Alan: Try annotate the MockConfiguration class with the @SpringBootApplication annotation instead, see update in my previous comment.

          1. Alan

            Getting further.. the problems i see now is that it’s failing to instantiate all the autowired beans in my app. Do i need to use an exclude filter for the purpose of this test? I also have to manually remove the @component annotation for the existing JerseyConfig i have that also extends ResourceConfig

          2. Mattias Severson

            Think about whether it is necessary to create a mocked application context for the entire application, or if you can create a separate mock application context that contains just enough beans to test one of your endpoints at the time. If you choose the former, you are probably better of by writing a full integration test (e.g. by using an embedded database). If you choose the latter, then it should be sufficient to just mock the autowired dependencies for the specific endpoint.

  9. Alan

    I’m having trouble using body response validation.

    Example:
    Json response is a based on a java List.

    body(“get(0).methodReturnsDouble”, Matchers.equalTo(1.1))

    java.lang.AssertionError: JSON path get(0).methodReturnsDouble doesn’t match.
    Expected:
    Actual: 1.1

    Do I have to convert to a String? or some other object? Having the same issue with BigDecimal matching.

    Thanks in advance for any help.

    1. Mattias Severson

      @Alan: Make sure that your application returns the expected response, for example by printing the it using log().all(). Next, make sure that you understand Rest Assured’s syntax for traversing the response body, start by reading one of the examples at the project page. There you will also find “Returning floats and doubles as BigDecimal” as well as Note on floats and doubles.

      1. Alan

        In my above example. the expected was 1.1 enclosed in diamond brackets <> but it looks like it was omitted above. Will check out the references… thanks!

  10. Vincent

    Thanks a lot for this post, works perfect and helped me a lot setting up what I want to do. In my case, I work regularly with Spring Batch or Spring Integration with a webservice output. Since I’m using Spring out of the box components, it’s difficult to mock, and it makes sense to startup a fake end point that will get all the requests that are sent, and assert their number and content after.

    In that context, do you see a way to use random port to start up the fake end point ? the main challenge is to make my Spring Batch/Integration app aware of this port at the right moment, ie after Tomcat has started, but before my app gets loaded.

    More details here : http://stackoverflow.com/questions/31089462/make-other-components-aware-of-webintegrationtest-random-port

    Thanks


    Vincent

    1. Mattias Severson

      @Vincent: I do not know how to create a dynamic solution for your integration tests. Meanwhile, I would probably use the server.port setting and hardcode it to use a specific port number that can be shared between projects.

  11. Urvi

    Hi Mattias,

    Thank you for providing this example. This is really good. Is there anyway we can parameterize CharacterControllerTest that you have mentioned in the example ? For instance, testing CharacterControllerTest class with different values.

    Thanks

    1. Mattias Severson

      @Urvi: I think that you will find the upcoming Spring 4.2 release interesting. The following lines are copied from the Spring 4.2 new features list:

      JUnit-based integration tests can now be executed with JUnit rules instead of the SpringJUnit4ClassRunner. This allows Spring-based integration tests to be run with alternative runners like JUnit’s Parameterized

      .
      There is already and RC2 release date of Spring 4.2 and the official release is scheduled next week (specifically, July 15 according to the Spring Framework Jira).

      However, I would think carefully before parameterizing integration tests. If you need to verify an algorithm, write (parameterized) unit tests for just the algorithm implementation. Likewise, I would consider implementing unit tests for input validation and JSON serialization and deserialization if I found it useful. Sometimes it also makes sense to write smaller integration tests to verify transaction or persistence by creating application contexts that only contains @Services that you can call directly from the tests without using HTTP requests.

  12. Prithvi Mandayam

    Hello,

    Thank you for this wonderful guide, it has really helped me. Now, I am using MongoDB for my database, and I would like to specify a different collection (or table) within in my database that is used solely for testing purposes. This way the content of my main collection is unaffected and I can add users and delete them however I want for testing purposes in a different collection.

    How would I go about doing this? Where would I specify the collection name that is to be used for the test runs?

    For the regular client application I specify the collection name on top of my model class with the @Document annotation such as:

    @Document(collection = “users”)
    public class User {
    private String name;

    }

    This way the entries are stored in the collections named “users”.

    How would I specify the collection used for testing purposes?

    Thank you,

    Prithvi Mandayam

    1. Mattias Severson

      @Prithvi: Thank you.

      I would use the same collection names for both test and production, because one purpose of your tests is to verify that your repository and @Document annotations work as expected. However, executing tests against the production database is not a good idea, so I would use different database instances in different environments. Compare how you test applications that use SQL, typically you have a dedicated test database that is separated from your production database (your suggestion would imply that you would use the same database instance and use different table names). You can run a standard MongoDB instance in the test environment and there is also an Embedded MongoDB that you may find useful (disclaimer: I have not tried it).

  13. George

    Hi, I have secured Jersey Endpoints using a combination of a ContainerRequestFilter and the RolesAllowedDynamicFeature. For integration testing is there a “easy” way to mock this or bypass this? I see i can use given().cookie to create a mock cookie needed by the filter. However, i also need to mock or set a rs.core.SecurityContext. Any suggestions on how to go about this.? Thanks for any advice /help

    1. Mattias Severson

      @George: If you are using Spring Boot you can autowire any Spring bean to your test class and thus change its internal state before the test (like I have done with the CharacterRepository in the setUp method above).
      Alternatively, you can create a specific MockApplicationContext in which you can create mocked beans. If you declare them @Primary Spring will give them higher priority than the beans in your normal application context, provided that you added the mock application context to the test, e.g. @SpringApplicationConfiguration(classes = {Application.class, MockApplicationContext.class}).

  14. Raghu

    hi,
    thanks for the great write up. We use a legacy container to deploy our app. Would it be possible use a tomcat 6 as the embedded container. Our web.xml is 2.5 servlet spec compliant

    thanks

    raghu

    1. Mattias Severson

      @Raghu: The standard Spring Boot configuration contains an embedded Servlet container (Tomcat 8 is the default, but if you read the Spring Boot reference docs you notice that you can also choose Tomcat 7 Jetty or Undertow). Consequently, my advice would be to upgrade the application to be at least Servlet 3 compliant (to be able to use Tomcat 7 or Jetty 8) or Servlet 3.1 to be able to use the latest Tomcat 8, Jetty 9 or Undertow. Tomcat has published a migration guide that you may find useful.

  15. Paulo

    Thanks a million. I am new to Spring Boot and I was having trouble finding a way to understand how to fire up integration tests. Your post is just what I needed.

    1. Mattias Severson

      @Paulo: You’re welcome! I’m glad that you found the post helpful.

  16. Sireesha

    Great article. I am looking for how to execute the integration tests using java command from the command line? ex – Just the way we start the java app.

    1. Mattias Severson

      @Sireesha: Thanks. These tests are no different from any other Spring integration tests. Typically, you add them to your Spring Boot project so that they are executed by tools such as Gradle or Maven as part of your build process. Alternatively, you can execute the tests from the command line as any other jUnit test (see “How do I run JUnit from my command window?” in the jUnit FAQ), but that requires more manual work since you need to manage the jUnit classpath, keep track of test class names and similar tasks that can be handled by a build tool.

  17. Eugene

    Thanks for post, very handy.
    But samples missing import statements
    what are dependencies for methods
    when().
    then().

    ???

    1. Mattias Severson

      @Eugene: Thank you. If you start by adding import static com.jayway.restassured.RestAssured.when; then you can tab-complete the other methods due to Rest Assured’s fluent DSL. See also the Static Imports paragraph in Rest Assured’s Usage Guide.

  18. Eugene

    Thanks for the response.
    I have already covered few of my services and it was way easier than using RestTemplate.
    Very simple to use, very compact and handy.

    picky one actually asked exactly what I was looking for – github link.
    I think you should publish link on github right at the top of your article.
    I would also advice you to provide code samples along with import statements in it.
    Having sample for maven or gradle dependencies won’t hurt as well.
    Though I got all this from your sample project.

  19. Pawel

    Hi,
    I think that you have forgotten to add these lines of code:

    @Autowired
    private WebApplicationContext context;

    @Before

    RestAssuredMockMvc.webAppContextSetup(context);

    1. Mattias Severson

      @Pawel: No, the purpose of this blog post is to show an integration test that uses the entire application stack, including the Servlet container. The test communicates with the application by sending true HTTP requests and HTTP responses. By using MockMvc, directly or wrapped by RestAssuredMockMvc, you do not need a running application container, the test will use a mock implementation of the servlet API, mocked requests and mocked responses. See also the second bullet in Considerations above.

  20. Roger

    Maybe I missed it but is this project or a similar one on github?
    Thanks

    1. Mattias Severson

      @Roger: If you by “this project” are referring to the code used in the blog post, I am afraid that the answer is no. However, all the bits and pieces are there, it should not take long to copy and paste the information to get a sample project running. If you mean Rest Assured, you will find it here.

  21. luis

    Thanks for the post and the explanation; really useful.

    There is something I don’t understand though. It’s about transaction based tests not being possible with this approach. Do you mean that it’s not possible to rollback transactions, so everything will be commited to DB? The doc that you point to (by the way, the link is half-broken) doesn’t say anything about limitations. Is the doc wrong, or what am I missing?

    1. Mattias Severson

      @luis: Thank you for your comment. The docs are correct if you write your traditional Spring integration test against a Spring application context. However, in the example above there is also the servlet container and the entire webstack between the HTTP request sent in the integration test and any transaction boundary defined in the application context. Consequently, any mutating database operation triggered by the request will be committed long before the integration test gets a chance to perform a rollback.

      Btw, I have updated the broken links, thanks.

  22. uv

    Nice example.
    However, please consider not cutting the imports away.
    finding the right static when import is not trivial in a different setup.

    1. Mattias Severson

      @uv: Thank you for your comment. Information about the static imports can be found in my reply to the first comment.

  23. Gopi

    Very useful and saves lot of my application setup time. Thank you.

  24. Don

    Hi Mattias, could you send the complete sample project source? Maybe I missed the link somewhere. I’m able to run the tests but all three fail:

    java.lang.AssertionError: 1 expectation failed.
    Expected status code doesn’t match actual status code .
    … {bulk of stacktrace omitted} …
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

    This is most likely due to the fact that the Character objects are not persisted to the DB during setup (my guess anyway). I added getter and setter methods to Character.java and included overloaded ctor that accepts name as arg. Thanks, Don

  25. Don

    Mattias, never mind. Objects are getting persisted to the DB, but just discovered that I needed several more RequestMappings. There was only one method with mapping to /characters/{id} …
    @RequestMapping(value = “/characters/{id}”, method = RequestMethod.DELETE)
    … in CharacterController.java and DELETE was expected, so tests were returning 404 instead of 200.

    By-the-way, great article!

    Don

    1. Mattias Severson

      @Don: Good that you solved your problems and that you found the blog post useful.

  26. drsquidop

    Great article! I really appreciate the detailed footnotes on the meaning of each annotation.

  27. kaushik

    Mattias,
    Great article and very useful tips. I am new to rest assured and I tried to run a simple test following above steps. I get connection refused errors. How do I inject port numbers in the url which I pass in rest assured?

    java.net.ConnectException: Connection refused: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:117)
    at ead.java:745)

    1. Mattias Severson

      @kaushik: In my example Spring Boot determines which port the app is deployed to, see the @IntegrationTest attribute configuration in point 4 above. Consequently, Spring Boot also need to notify the test app which port that was actually used, see @Value("${local.server.port}"), see point 6.

      If your server is deployed to a specific port, you can configure Rest Assured to always use the same port by setting the RestAssured.port, see point 9. Alternatively, you can use Rest Assured’s fluent DSL like given().port(80). ... or ..when().get("http://myhost.org:80/doSomething");, see Rest Assured’s usage guide.

  28. Annex

    I am not able to import class for @IntegrationTest(“server.port:0”)
    Tried many dependencies. Can you help

    1. Mattias Severson

      @Annex: Did you see the list of dependencies? In particular I used the spring-boot-starter-test 1.1.3.RELEASE dependency. Since then, a number of Spring Boot releases have been made and if you read the JavaDoc of @IntegrationTest you can see that it has been deprecated as of Spring Boot version 1.4 in favor of @SpringBootTest, but it should still work using version 1.4.

  29. Clint

    Really appreciate this article. It’s helped launch me into unit testing my Spring Boot Controller classes with real rest requests – something which I thought would be too difficult to do given the amount of effort to make request.

    Rest-Assured and this example has made it trivial :)

  30. worryNerd

    Scenario: Service A calls service B. My integration tests are written on ServiceA . I have created a mock service using wiremock ,MockServiceB. How can I use my mock service inplace of ServiceB? My application is written using springboot and gradle. Any pointers.?

    1. Mattias Severson

      @worryNerd: First, start MockServiceB. Make note of MockServiceB’s url and change the configuration of ServiceA to point to the mock url instead of ServiceB’s production url. Next, launch ServiceA and execute your integration tests against ServiceA. Make sure to configure the services to use different ports if both ServiceA and MockServiceB are running on localhost.

      1. worryNerd

        Thanks Mattias. The mock service resides in the same proj as my tests . The dev team in particular dsn’t want the prod url to be replaced with the mock service inplace they suggest that the tests should just hit the mock which I am unable to perform. Maybe have a different profile in gradle to have this division work out.? Just throwing an idea

        1. Mattias Severson

          @worryNerd: Well, unless you replace ServiceB’s url with the MockServiceB’s url you will always hit ServiceB’s production url when you run the tests against ServiceA (regardless if you run ServiceA on localhost). Normally, you make things such as database usernames, passwords, endpoints, etc configurable so that you can change them in your test environment and the same goes for the dependent service url. Since you found this blog post, I assuming that you are using Spring Boot. In the reference docs, you will find a long list of how you can externalize the configuration and which configuration option takes precedence if you need to override a value.

  31. worryNerd

    Hello, I am facing a problem when using restassured lib with springboot test annotation.
    The error that I am getting in my test is :
    java.lang.IllegalArgumentException: URI must not be null
    at org.springframework.util.Assert.notNull(Assert.java:134) ~[spring-core-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.web.util.UriComponentsBuilder.fromUriString(UriComponentsBuilder.java:189) ~[spring-web-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.web.util.DefaultUriTemplateHandler.initUriComponentsBuilder(DefaultUriTemplateHandler.java:114) ~[spring-web-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.web.util.DefaultUriTemplateHandler.expandInternal(DefaultUriTemplateHandler.java:103) ~[spring-web-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.web.util.AbstractUriTemplateHandler.expand(AbstractUriTemplateHandler.java:106) ~[spring-web-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:612) ~[spring-web-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:407) ~[spring-web-4.3.10.RELEASE.jar:4.3.10.RELEASE]

    1. Mattias Severson

      @worryNerd: The error that you have encountered is not related to this blog post. Looking at the stacktrace, the error seems to origin from your usage of RestTemplate (and not Rest Assured), so I suggest that you start debugging there.

      1. worryNerd

        Thanks @Mattias, yes I debugged and it is RestTemplate error.

  32. borisivan

    Hi Mattias, great blog post — it was helpful for when I was putting together integration tests against an external deployment. Specifically, I have a Maven project that is running tests against an external deployed product, and some of the Maven dependencies are the services themselves, as I use them for some of the actions associated with setup, or some of the test steps themselves. Those services are springboot applications.
    I’m having a problem after trying to change my test project to use springboot 1.5.8. (was previously 1.3.1, with spring-test 4.2.4).

    With 1.3.1, this was successful:

    public class baseClass extends AbstractTestNGSpringContextTests

    @SpringApplicationConfiguration(classes = xxConfiguration.class)
    public class xx extends TestBaseClass {

    @EnableAutoConfiguration
    @ComponentScan(basePackages = { “com.xx.xx” })
    @Configuration
    public class xxConfiguration {
    }

    And a spring-test-config.xml file:

    This worked great, and I could use any of the service artifacts as dependencies, and just needed to have those services override the endpoint URL with a system property, and all service methods were available to me, interacting with the external system.

    After upgrading springboot to 1.5.8, I had to change @SpringApplicationConfiguration to @SpringBootTest, and I can’t seem to work around:

    [ WARN GenericWebApplicationContext ] Exception encountered during context initialization – cancelling refresh attempt: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.xxx.xx.xxConfiguration]; nested exception is java.io.FileNotFoundException: class path resource [org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.class] cannot be opened because it does not exist

    Any suggestions? I’m thinking it’s something to do with the context setting, but I don’t know how to proceed.

    1. Mattias Severson

      @borisivan: Thanks. Have you checked the release notes of Spring Boot 1.4 and Spring Boot 1.5? Specifically, I guess that the Test utilities and classes paragraph in the Spring Boot 1.4 will be of interest for your case (and the link to the 40.3 Testing Spring Boot applications in the Spring Boot 1.4 reference docs. Another trick is to update with one of the Spring Boot 1.4.x versions as an intermediate step before updating to version 1.5.8. Look for deprecations in the code (some IDE:s provide tooling support) and compile warnings and read the JavaDocs of the corresponding classes. The Spring Team usually provides migrations hints so that developers can move away from the deprecations.

  33. priya

    very informative blog , It makes it easier to start a project with a default setup that can be customised as you need. thanks for sharing.

  34. Omar Salem

    great article, thank you

  35. Kurt

    Found this via a stackoverflow question.
    I’m having issues with the following error:
    Caused by: java.lang.ClassNotFoundException: org.springframework.boot.Banner$Mode. I assume this is caused by not having the correct dependencies in my pom file. However, I can’t seem to find the right one for Banner. My search directed me to spring-boot-legacy artifact, but that doesn’t seem to do the trick. Still getting the same error.

    Any advice to resolve this issue? Is there a better / newer artifact that I should use instead?
    Thanks

    1. Mattias Severson

      @Kurt: You are probably correct in your assumption that the dependencies need to be updated. Spring Boot uses semantic versioning and an increment of the major version number indicates that there are breaking API changes. This blogpost was written more than four years ago and if you look at the dependencies paragraph you will see that I used Spring Boot version 1.1.3 back then. If I am not mistaken, the latest Spring Boot release is at the time of writing version 2.1.0.

Leave a Reply