Series:
Goal:
·
We are going to build on top of Service Layer
(that we covered in the previous post.)
·
We are going to expose Service Layer as RESTful
APIs.
·
We are going to use TDD to develop the RESTful
service functionality.
·
We are going to use Jackson which returns JSON
object in the RESTful response.
Steps:
1.
add dependencies to pom.xml
a.
web, mvc, Jackson, selenium, etc.
2.
Amend context.xml to include MVC
3.
Amend web.xml to include RESTful/Servlet
mapping, etc.
4.
Create a POJO which exposes the service layer.
a.
Accepts HTTP requests (standard HTTP verbs,
POST, PUT, GET, etc.)
b.
Invokes service layer
c.
Returns response as JSON object.
5.
Create a Test class which uses Selenium to fire
HTTP request with desired RESTful service with proper Verb (we will demonstrate
a GET call.)
6.
Run the Test Suite
Step 1: Add dependencies
For Spring MVC + JSON related stuff + Selenium (the exact
packages are mentioned below in Appendex.)
Step 2: Amend context.xml
Ensure that all
relevant namespace and xsd references are included:
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/oxm
http://www.springframework.org/schema/oxm/spring-oxm-4.1.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
Scan the RESTFul web layer package:
<context:component-scan base-package="com.mycos.dpweb.ws" />
Add JSON handler:
<!-- Configures
view for returning JSON to the client -->
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
<property name="contentType" value="text/plain"/>
</bean>
<!--
maps handler methods based on HTTP
paths
-->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list id="beanList">
<ref bean="jsonMessageConverter"/>
</util:list>
</property>
</bean>
<!--
Converts JSON to POJO and vice
versa
-->
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
<bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/>
</bean>
Step 3: Amend web.xml
<!-- Main
configuration file for this Spring web application. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/context.xml</param-value>
</context-param>
<!-- Loads the
Spring web application context using the config file defined
above.
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Define the
Spring Dispatcher Servlet for the REST services. The
'contextConfiguration'
param
with an empty value means that the Spring Context won't try to load
a
default file called restservices-servlet.xml -->
<servlet>
<servlet-name>restservices</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- This Servlet
mapping means that this Servlet will handle all incoming
requests -->
<servlet-mapping>
<servlet-name>restservices</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
Step 4: Create POJO for RESTFul service
1.
Create a package and a Java class
2.
Annotate with @Controller
3.
Inject Service Layer Object
4.
Inject “View”
5.
Inject @RequestMapping wherever is needed.
Please note that for simplicity I have eliminated the detail steps
to get to the code. But essentially you start with a blank template method,
implement unit test cases, and then start adding flesh to the template.
Code:
@Controller
public class XxxConroller {
@Autowired
private XxxService xxxService;
@Autowired
private View jsonView_i;
@RequestMapping(value = "/user/{userId}", method =
RequestMethod.GET)
public ModelAndView
getUserID(@PathVariable("userId") String userId) {
/* validate user Id
parameter */
if (isEmpty(userId)) {
String
sMessage = "Error
invoking getUserById - Invalid user Id parameter";
return createErrorResponse(sMessage);
}
try {
return new ModelAndView(jsonView_i, DATA_FIELD, this.xxxService.getUser(userId));
}
catch (Exception e) {
String
sMessage = "Error
invoking getAllLocations. [%1$s]";
return
createErrorResponse(String.format(sMessage, e.toString()));
}
}
Step 5: Create Test Class.
1.
Create a package and a class.
public class XxxRESTAPITest extends XxxDBUnitTest{
//extend for the DBUnitTest classes created in the previous post
Create a properties file
with expected value. Test case is going to use this value to compare with the
json object returned by the RESTful API call. ( ClassPathResource
locationTblContent = new ClassPathResource("/webtest/webtest.properties");
2.
Handle JSON object.
·
You can store the entire json object (john_expected_data={"data"\:{"userId"\:"john","isxxx"\:"Y"}})
or
·
You can store only the relevant value and parse
the JSON object from the invoked RESTful call to assert a success or
unsuccessful call.
@Test
public void
testgetUserByIDRESTAPI() throws Exception {
init();
this.testHTML("user/john/", this.prop.getProperty("john_expected_data"));
}
StringBuilder
strBldr = new StringBuilder();
strBldr.append(prop.get("url"));
strBldr.append(uri);
WebDriver driver = new FirefoxDriver();
driver.get(strBldr.toString());
log.debug(driver.findElement(By.tagName("body")).getText());
assertEquals(expectedValue, driver.findElement(By.tagName("body")).getText());
driver.close();
driver.quit();
Appendix:
Pom.xml (relevant jar
files)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- for Spring WEB/VMC/REST
-->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson.mapper.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.fasterxml.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.fasterxml.version}</version>
</dependency>
<!-- for Selenium;
testing HTTP request/response -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selecnium.version}</version>
</dependency>
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>${xml-api.version}</version>
</dependency>
No comments:
Post a Comment