Friday, August 28, 2015

Integration tests in SpringBoot without external dependencies

Target - test all layers of SpringBoot REST application in isolation from external components.

Complexity - typical application uses database and makes calls to remote services.

Solutions:

a) Use spring-test support for integration tests. It actually starts whole app for you on random port. At the same time, all components of app can be wired into test for additional manipulations.

b) Use RestAssured to make calls to our application

c) Use RestTemplate to make remote calls and MockServer from spring-test to mock them

d) Use in-memory database. HSQLDB does the simulating job pretty well.

e) Use separate Spring profile to tweak app configuration


Here is a small example putting it all together:


import com.jayway.restassured.RestAssured
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.SpringApplicationConfiguration
import org.springframework.boot.test.WebIntegrationTest
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.web.client.MockRestServiceServer
import org.springframework.web.client.RestTemplate
import spock.lang.Specification
import static com.jayway.restassured.RestAssured.given
import static com.jayway.restassured.http.ContentType.JSON
import static org.springframework.http.HttpMethod.*
import static org.springframework.http.MediaType.APPLICATION_JSON
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess
@SpringApplicationConfiguration(classes = Application)
@WebIntegrationTest(["server.port=0"]) //will actually start app on random port
@ActiveProfiles(["integration"]) //adapt some configuration for tests
class TrustlyPayoutSpec extends Specification {
@Value('${local.server.port}')
int port //random port chosen by spring test
@Autowired
Repository repository //we can wire any repository to run DB checks
@Autowired
RestTemplate remoteResourceRestTemplate //wire RestTemplate to instrument it with mock server
MockRestServiceServer mockRemoteResource
def setup() {
mockRemoteResource = MockRestServiceServer.createServer(remoteResourceRestTemplate) //instrument RestTemplate
RestAssured.port = port //configure RestAssured port
}
def "Some integration scenario"() {
expect:
//stub calls to remote resource using mock server
mockRemoteResource.expect(requestTo("https://example.com"))
.andExpect(method(PUT))
.andExpect(jsonPath('$.state').value("TRANSFERRED"))
.andRespond(withSuccess(
""" {"status": "OK"} """
, APPLICATION_JSON))
//call our service using RestAssured
given().contentType(JSON).body(""" { "name": "John Doe" } """)
.when().post("/user")
.then().statusCode(CREATED.value())
Thread.sleep(5000) //give some time for JMS messaging and async processing
User user = repository.findByName("John Doe") //check data in DB
//some assertions on DB data
mockRemoteResource.verify()
}
}

2 comments:

  1. Hello Aleksandr,
    The informative Article on Integration tests in SpringBoot without external dependencies is good. It gives detailed information about it .Thanks for Sharing the information unit testing and integration testing. Software Testing Services

    ReplyDelete
  2. Amplework Software Pvt. Ltd. offer flexible models to hire expert Swift app developers and programmers on hourly, part-time, or full-time contract basis at affordable cost.



    Product Design
    MVP Development
    DevOps Solutions
    Mobile Application Development
    Voice/Alexa App Development
    IoT App Development

    ReplyDelete