Ref:- https://spring.io/guides/gs/rest-service/
Friday, January 15, 2016
Consuming a RESTful Web Service using Spring RESTTemplate
REf:- https://spring.io/guides/gs/consuming-rest/
What is @WebAppConfiguration in Spring MVC
Ref:- http://docs.spring.io/spring-framework/docs/3.2.0.BUILD-SNAPSHOT/api/org/springframework/test/context/web/WebAppConfiguration.html
@WebAppConfiguration
is a class-level annotation that is used to declare that the ApplicationContext
loaded for an integration test should be a WebApplicationContext
.
The mere presence of
@WebAppConfiguration
on a test class ensures that a WebApplicationContext
will be loaded for the test using a default for the path to the root of the web application. To override the default, specify an explicit resource path via the value()
attribute.
Note that
@WebAppConfiguration
must be used in conjunction with @ContextConfiguration
, either within a single test class or within a test class hierarchy.Unit Testing of Spring MVC
Ref:- http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/htmlsingle/#spring-mvc-test-framework
Spring MVC Test Framework
Standalone project
Before inclusion in Spring Framework 3.2, the Spring MVC Test framework had already existed as a separate project on GitHub where it grew and evolved through actual use, feedback, and the contribution of many.
The standalone spring-test-mvc project is still available on GitHub and can be used in conjunction with Spring Framework 3.1.x. Applications upgrading to 3.2 should replace the
spring-test-mvc
dependency with a dependency onspring-test
.
The
spring-test
module uses a different packageorg.springframework.test.web
but otherwise is nearly identical with two exceptions. One is support for features new in 3.2 (e.g. asynchronous web requests). The other relates to the options for creating a MockMvc
instance. In Spring Framework 3.2, this can only be done through the TestContext framework, which provides caching benefits for the loaded configuration.
The Spring MVC Test framework provides first class JUnit support for testing client and server-side Spring MVC code through a fluent API. Typically it loads the actual Spring configuration through theTestContext framework and always uses the
DispatcherServlet
to process requests thus approximating full integration tests without requiring a running Servlet container.
Client-side tests are
RestTemplate
-based and allow tests for code that relies on the RestTemplate
without requiring a running server to respond to the requests.
Before Spring Framework 3.2, the most likely way to test a Spring MVC controller was to write a unit test that instantiates the controller, injects it with mock or stub dependencies, and then calls its methods directly, using a
MockHttpServletRequest
and MockHttpServletResponse
where necessary.
Although this is pretty easy to do, controllers have many annotations, and much remains untested. Request mappings, data binding, type conversion, and validation are just a few examples of what isn't tested. Furthermore, there are other types of annotated methods such as
@InitBinder
,@ModelAttribute
, and @ExceptionHandler
that get invoked as part of request processing.
The idea behind Spring MVC Test is to be able to re-write those controller tests by performing actual requests and generating responses, as they would be at runtime, along the way invoking controllers through the Spring MVC
DispatcherServlet
. Controllers can still be injected with mock dependencies, so tests can remain focused on the web layer.
Spring MVC Test builds on the familiar "mock" implementations of the Servlet API available in the
spring-test
module. This allows performing requests and generating responses without the need for running in a Servlet container. For the most part everything should work as it does at runtime with the exception of JSP rendering, which is not available outside a Servlet container. Furthermore, if you are familiar with how the MockHttpServletResponse
works, you'll know that forwards and redirects are not actually executed. Instead "forwarded" and "redirected" URLs are saved and can be asserted in tests. This means if you are using JSPs, you can verify the JSP page to which the request was forwarded.
All other means of rendering including
@ResponseBody
methods and View
types (besides JSPs) such as Freemarker, Velocity, Thymeleaf, and others for rendering HTML, JSON, XML, and so on should work as expected, and the response will contain the generated content.
Below is an example of a test requesting account information in JSON format:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("test-servlet-context.xml") public class ExampleTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } @Test public void getAccount() throws Exception { this.mockMvc.perform(get("/accounts/1").accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.name").value("Lee")); } }
The test relies on the
WebApplicationContext
support of the TestContext framework. It loads Spring configuration from an XML configuration file located in the same package as the test class (also supports JavaConfig) and injects the created WebApplicationContext
into the test so a MockMvc
instance can be created with it.
The
MockMvc
is then used to perform a request to "/accounts/1"
and verify the resulting response status is 200, the response content type is "application/json"
, and response content has a JSON property called "name" with the value "Lee". JSON content is inspected with the help of Jayway's JsonPath project. There are lots of other options for verifying the result of the performed request and those will be discussed later.
The fluent API in the example above requires a few static imports such as
MockMvcRequestBuilders.*
, MockMvcResultMatchers.*
, and MockMvcBuilders.*
. An easy way to find these classes is to search for types matching "MockMvc*". If using Eclipse, be sure to add them as "favorite static members" in the Eclipse preferences under Java -> Editor -> Content Assist -> Favorites. That will allow use of content assist after typing the first character of the static method name. Other IDEs (e.g. IntelliJ) may not require any additional configuration. Just check the support for code completion on static members.
The goal of server-side test setup is to create an instance of
MockMvc
that can be used to perform requests. There are two main options.
The first option is to point to Spring MVC configuration through the TestContext framework, which loads the Spring configuration and injects a
WebApplicationContext
into the test to use to create a MockMvc
:@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("my-servlet-context.xml") public class MyWebTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } // ... }
The second option is to simply register a controller instance without loading any Spring configuration. Instead basic Spring MVC configuration suitable for testing annotated controllers is automatically created. The created configuration is comparable to that of the MVC JavaConfig (and the MVC namespace) and can be customized to a degree through builder-style methods:
public class MyWebTests { private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); } // ... }
Which option should you use?
The "webAppContextSetup" loads the actual Spring MVC configuration resulting in a more complete integration test. Since the TestContext framework caches the loaded Spring configuration, it helps to keep tests running fast even as more tests get added. Furthermore, you can inject mock services into controllers through Spring configuration, in order to remain focused on testing the web layer. Here is an example of declaring a mock service with Mockito:
<bean id="accountService" class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="org.example.AccountService"/> </bean>
Then you can inject the mock service into the test in order set up and verify expectations:
@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("test-servlet-context.xml") public class AccountTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Autowired private AccountService accountService; // ... }
The "standaloneSetup" on the other hand is a little closer to a unit test. It tests one controller at a time, the controller can be injected with mock dependencies manually, and it doesn't involve loading Spring configuration. Such tests are more focused in style and make it easier to see which controller is being tested, whether any specific Spring MVC configuration is required to work, and so on. The "standaloneSetup" is also a very convenient way to write ad-hoc tests to verify some behavior or to debug an issue.
Just like with integration vs unit testing, there is no right or wrong answer. Using the "standaloneSetup" does imply the need for some additional "webAppContextSetup" tests to verify the Spring MVC configuration. Alternatively, you can decide write all tests with "webAppContextSetup" and always test against actual Spring MVC configuration.
To perform requests, use the appropriate HTTP method and additional builder-style methods corresponding to properties of
MockHttpServletRequest
. For example:mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
In addition to all the HTTP methods, you can also perform file upload requests, which internally creates an instance of
MockMultipartHttpServletRequest
:mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8")));
Query string parameters can be specified in the URI template:
mockMvc.perform(get("/hotels?foo={foo}", "bar"));
Or by adding Servlet request parameters:
mockMvc.perform(get("/hotels").param("foo", "bar"));
If application code relies on Servlet request parameters, and doesn't check the query string, as is most often the case, then it doesn't matter how parameters are added. Keep in mind though that parameters provided in the URI template will be decoded while parameters provided through the
param(...)
method are expected to be decoded.
In most cases it's preferable to leave out the context path and the Servlet path from the request URI. If you must test with the full request URI, be sure to set the
contextPath
and servletPath
accordingly so that request mappings will work:mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
Looking at the above example, it would be cumbersome to set the contextPath and servletPath with every performed request. That's why you can define default request properties when building the
MockMvc
:public class MyWebTests { private MockMvc mockMvc; @Before public void setup() { mockMvc = standaloneSetup(new AccountController()) .defaultRequest(get("/") .contextPath("/app").servletPath("/main") .accept(MediaType.APPLICATION_JSON).build(); } }
The above properties will apply to every request performed through the
MockMvc
. If the same property is also specified on a given request, it will override the default value. That is why, the HTTP method and URI don't matter, when setting default request properties, since they must be specified on every request.
Expectations can be defined by appending one or more
.andExpect(..)
after call to perform the request:mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
MockMvcResultMatchers.*
defines a number of static members, some of which return types with additional methods, for asserting the result of the performed request. The assertions fall in two general categories.
The first category of assertions verify properties of the response, i.e the response status, headers, and content. Those are the most important things to test for.
The second category of assertions go beyond the response, and allow inspecting Spring MVC specific constructs such as which controller method processed the request, whether an exception was raised and handled, what the content of the model is, what view was selected, what flash attributes were added, and so on. It is also possible to verify Servlet specific constructs such as request and session attributes. The following test asserts that binding/validation failed:
mockMvc.perform(post("/persons")) .andExpect(status().isOk()) .andExpect(model().attributeHasErrors("person"));
Many times when writing tests, it's useful to dump the result of the performed request. This can be done as follows, where
print()
is a static import fromMockMvcResultHandlers
:mockMvc.perform(post("/persons")) .andDo(print()) .andExpect(status().isOk()) .andExpect(model().attributeHasErrors("person"));
As long as request processing causes an unhandled exception, the
print()
method will print all the available result data to System.out
.
In some cases, you may want to get direct access to the result and verify something that cannot be verified otherwise. This can be done by appending
.andReturn()
at the end after all expectations:MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn(); // ...
When all tests repeat the same expectations, you can define the common expectations once when building the
MockMvc
:standaloneSetup(new SimpleController()) .alwaysExpect(status().isOk()) .alwaysExpect(content().contentType("application/json;charset=UTF-8")) .build()
Note that the expectation is always applied and cannot be overridden without creating a separate
MockMvc
instance.
When JSON response content contains hypermedia links created with Spring HATEOAS, the resulting links can be verified:
mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people"));
When XML response content contains hypermedia links created with Spring HATEOAS, the resulting links can be verified:
Map<String, String> ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom"); mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML)) .andExpect(xpath("/person/ns:link[@rel='self']/@href", ns).string("http://localhost:8080/people"));
When setting up a
MockMvc
, you can register one or more Filter
instances:mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();
Registered filters will be invoked through
MockFilterChain
from spring-test
and the last filter will delegates to the DispatcherServlet
.
The framework's own tests include many sample tests intended to demonstrate how to use Spring MVC Test. Browse these examples for further ideas. Also the spring-mvc-showcase has full test coverage based on Spring MVC Test.
Client-side tests are for code using the
RestTemplate
. The goal is to define expected requests and provide "stub" responses:RestTemplate restTemplate = new RestTemplate(); MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate); mockServer.expect(requestTo("/greeting")).andRespond(withSuccess("Hello world", "text/plain")); // use RestTemplate ... mockServer.verify();
In the above example,
MockRestServiceServer
-- the central class for client-side REST tests -- configures the RestTemplate
with a customClientHttpRequestFactory
that asserts actual requests against expectations and returns "stub" responses. In this case we expect a single request to "/greeting" and want to return a 200 response with "text/plain" content. We could define as many additional requests and stub responses as necessary.
Once expected requests and stub responses have been defined, the
RestTemplate
can be used in client-side code as usual. At the end of the testsmockServer.verify()
can be used to verify that all expected requests were performed.
Just like with server-side tests, the fluent API for client-side tests requires a few static imports. Those are easy to find by searching "MockRest*". Eclipse users should add
"MockRestRequestMatchers.*"
and "MockRestResponseCreators.*"
as "favorite static members" in the Eclipse preferences under Java -> Editor -> Content Assist -> Favorites. That allows using content assist after typing the first character of the static method name. Other IDEs (e.g. IntelliJ) may not require any additional configuration. Just check the support for code completion on static members.
Spring MVC Test's own tests include example tests of client-side REST tests.
Wednesday, January 13, 2016
Injecting a prototype bean into a singleton bean
In Spring, most of the beans we work with are Singletons. If a singleton bean is wired with yet another singleton bean, there is absolutely no problem. But if it is wired with a bean which is of different scope, say prototype, how does it work? Here is the example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| public class RequestProcessor { private RequestValidator validator; public void handleRequest(String requestId){ validator.validate(requestId); // Process the request and update } public RequestValidator getValidator() { return validator; } public void setValidator(RequestValidator validator) { this .validator= validator; } } public class RequestValidator { private List<String> errorMessages = new ArrayList<String>(); public RequestValidator() { System.out.println( "Validator instance created!" ); } // Validates the request and populates error messages public void validate(String requestId){ } public List<String> getErrorMessages() { return errorMessages; } } |
And here is the spring configuration:
1
2
3
4
5
| < bean id = "requestProcessor" class = "com.rajesh.learnjava.RequestProcessor" > < property name = "validator" ref = "validator" /> </ bean > < bean id = "validator" scope = "prototype" class = "com.rajesh.learnjava.RequestValidator" /> |
With this configuration, it is expected that when ever I fetch requestProcessor from application context, it will be wired with a new validator as we declared the validator bean is of prototype scope. But this does not happen.
When the application context gets initialized, it sees that requestProcessor is a singleton bean and initializes it to the context after wiring it with all the dependencies set. So from then onwards when we request context for requestProcessor, it return the same bean every time. To solve this issue, we have 2 approaches:
1. Lookup Method injection: For this, we have to declare the beans as follows:
1
2
3
4
5
6
| < bean id = "requestProcessor" class = "com.rajesh.learnjava.RequestProcessor" > < lookup-method name = "getValidator" bean = "validator" /> </ bean > < bean id = "validator" scope = "prototype" class = "com.rajesh.learnjava.RequestValidator" /> |
The Spring Framework implements method injection by using CGLIB library to generate dynamically a subclass that overrides the method. So for the method to be overridden, we have to define that method in the class and either provide a dummy implementation for it or make it abstract. Making a method abstract implies that class also has to be made abstract which will make it difficult to unit test. So providing a dummy implementation is a better choice.
Whenever we define a bean with lookup methods, Spring creates a subclass of the bean and overrides those methods which are marked as lookup-methods. And this subclassed bean gets registered into the context. The subclass delegates all the non-lookup methods to the original class. For the lookup methods, it overrides the implementation. So in our example, when getValidator() is called, it returns a new validator instance.
We can roughly imagine our new subclass(registered in container) like this:
1
2
3
4
5
| requestProcessor = new RequestProcessor(){ public RequestValidator getValidator(){ return context.getBean( "validator" ); } }; |
We could have directly fetched the bean from application context in RequestProcessor itself. But this would mean that the class is directly coupled to Spring framework. To do this in a cleaner way, we can use lookup injection. This puts all the spring related stuff at one place.
2. Scoped Proxies: This can be implemented as:
1
2
3
4
5
6
7
8
9
| < bean id = "requestProcessor" class = "com.rajesh.learnjava.RequestProcessor" > < property name = "validator" ref = "validator" /> </ bean > < bean id = "validator" scope = "prototype" class = "com.rajesh.learnjava.RequestValidator" > <!-- This instructs the container to proxy the current bean--> < aop:scoped-proxy /> </ bean > |
Remember, in case of look up method injection, proxy is created for singleton bean. But in case of scoped proxies, proxy is created for prototype bean and wired into the singleton bean during the process of registering the singleton bean in the context. The proxy thus created understands the scope and returns instances based on the requirements of the scope. So in our case, requestProcessor holds a reference to proxy in place of validator.
And in case of lookup method injection, when requestProcessor gets loaded into the context, validator will not be initialized at all. And when we call the look up method, it returns the prototype bean. But instead of calling the method, if you try to directly access the prototype bean(assuming it is accessible), it gives a Nullpointer Exception as it didn’t get initialized(We are not wiring it using property tag of bean)
In case of this, we can also configure how a proxy can be created. It can be done in 2 ways
1. CGLIB library which directly subclasses the object. This is the default option of Spring. For this, we must have CGLIB library our class path.
2. Java Dynamic Proxies. For this to be activated, we have to call:
1. CGLIB library which directly subclasses the object. This is the default option of Spring. For this, we must have CGLIB library our class path.
2. Java Dynamic Proxies. For this to be activated, we have to call:
1
| < aop:scoped-proxy proxy-target-class = "false" /> |
Here in this case, we don’t need any additional libraries in our class path. But the scoped bean must implement at least one interface and it has to be referred through the same interface at all places in order to get itself wired.
Few points to note:
1. Both method injection and scoped proxies work not only for prototype beans. This works more generic. Whenever a bean of different scope is injected into a singleton bean, we can use any of these techniques to ensure that we get a corresponding scope object.
2. Note that in the proxy, the method returning the prototype bean is overridden to return a new instance for every single call.
Suppose we want to display the error messages that we have got after validation:
1. Both method injection and scoped proxies work not only for prototype beans. This works more generic. Whenever a bean of different scope is injected into a singleton bean, we can use any of these techniques to ensure that we get a corresponding scope object.
2. Note that in the proxy, the method returning the prototype bean is overridden to return a new instance for every single call.
Suppose we want to display the error messages that we have got after validation:
1
2
3
4
5
| requestProcessor.getValidator().validate(); for (String message: requestProcessor.getValidator().getErrorMessages()){ logger.log(LogLevel.ERROR, message); } |
This code seems to print the error messages we have got after validation process. But this will never print any error messages even if there are many validation failures. This happens because requestProcessor.getValidator() returns a new validator instance every time it is called. So for this to work, the code has to be modified as:
1
2
3
4
5
6
| RequestValidator validator = requestProcessor.getValidator(); validator.validate(); for (String message: validator.getErrorMessages()){ logger.log(LogLevel.ERROR, message); } |
This happens only in case of prototype beans but works perfectly in case of other non-singleton scopes(request, session, global-session).
Subscribe to:
Posts (Atom)