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 value0
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 thecanDeletePluto()
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
- Testing Spring Boot applications
- Use a random unassigned HTTP port
- Discover the HTTP port at runtime
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.
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)?
@Tony: Both methods are part of Rest Assured, specifically the
when()
method is a static method of the RestAssured class and thethen()
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.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();
}
@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:
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?
@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.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)
@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
orspring-boot-starter-parent
you can omit the version number (since it is defined inspring-boot-starter-parent
). Simply add just thejstl
to yourdependencies
:Otherwise, at current date, you also need to add
1.2
as version number.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.
@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.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.
@Alex: My pleasure. Glad that you solved your problem.
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.
@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.can you provide an example of what separate mock application context would look like? I’m new to spring and spring boot !
@Alan:
Something like this (assuming that you are using Mockito):
and in your test (assuming you are using Spring Boot, embedded servlet container and Rest Assured as above):
Read more about Mockito in its documentation.
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
@Alan: Try annotate the
MockConfiguration
class with the@SpringBootApplication
annotation instead, see update in my previous comment.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
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.
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.
@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.
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!
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
@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.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
@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:
.
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
@Service
s that you can call directly from the tests without using HTTP requests.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
@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).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
@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 thesetUp
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})
.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
@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.
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.
@Paulo: You’re welcome! I’m glad that you found the post helpful.
Pingback: itemprop="name">How to unit test Java servlets | T. C. Mits 108
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.
@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.
The link in “9. We instruct Rest Assured” still points to the google code site but should point to https://github.com/jayway/rest-assured now.
@picky one: Updated, thanks!
Thanks for post, very handy.
But samples missing import statements
what are dependencies for methods
when().
then().
???
@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.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.
Hi,
I think that you have forgotten to add these lines of code:
@Autowired
private WebApplicationContext context;
@Before
…
RestAssuredMockMvc.webAppContextSetup(context);
@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 byRestAssuredMockMvc
, 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.Maybe I missed it but is this project or a similar one on github?
Thanks
@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.
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?
@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.
Nice example.
However, please consider not cutting the imports away.
finding the right static when import is not trivial in a different setup.
@uv: Thank you for your comment. Information about the static imports can be found in my reply to the first comment.
Very useful and saves lot of my application setup time. Thank you.
@Gopi: You are welcome.
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
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
@Don: Good that you solved your problems and that you found the blog post useful.
Great article! I really appreciate the detailed footnotes on the meaning of each annotation.
@drsquidop: Thanks, you are welcome!
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)
@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 likegiven().port(80). ...
or..when().get("http://myhost.org:80/doSomething");
, see Rest Assured’s usage guide.I am not able to import class for @IntegrationTest(“server.port:0”)
Tried many dependencies. Can you help
@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.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 :)
@Clint: Thank you, glad to help.
Check this project out on github: Complete sample project with springboot, angular and jwt – https://github.com/mrin9/Angular-SpringBoot-REST-JWT
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.?
@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.
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
@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.
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]
@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.Thanks @Mattias, yes I debugged and it is RestTemplate error.
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.
@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.
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.
@priya: Thanks, glad to help.
great article, thank you
@Omar: Thanks, you are welcome.
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
@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.