Not so long ago I was asked to write test application for working with Loans. Conditions :
1. XML-less spring
2. SpringMVC for rests
3. Two entity in application:Loan and User. Loan Can be extended with rate 1.5*previous rate. In case of more than 3 operation from one IP - reject operation.
4. Code have to be covered by unit and integration tests with ability for being executed from command line.
May be it will be useful for somebody, so I decided to publish it. Created application is very big so in post I will show only main classes, but entire application can be downloaded by link provided at the end of the post.
1. pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demien</groupId>
<artifactId>springmvcrest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Spring MVC Rest example</name>
<properties>
<spring.version>3.2.0.RELEASE</spring.version>
<jetty.version>8.1.15.v20140411</jetty.version>
<slf4j.version>1.7.5</slf4j.version>
<hibernate.version>4.2.5.Final</hibernate.version>
<h2.version>1.3.173</h2.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<integration.test.package>**/com/demien/springmvcrest/integration/test/**</integration.test.package>
</properties>
<dependencies>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<!-- HIBERNATE -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- H2 DB -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- NOXML Sping library -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
<!-- Jetty embedded -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- JSON marshaller -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.11</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.11</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.3</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
<build>
<finalName>springmvcrest</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.13</version>
<configuration>
<includes>
<include>**/*IT.java</include>
</includes>
</configuration>
<executions>
<execution>
<id>failsafe-integration-tests</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>integration</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<excludes>
<exclude>**/*$*</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
2. domains
Domains objects are very simple, so only one(of two) will be shown.
First, simple interface for entities which have to be stored in DB:
public interface IPersistable {
Integer getId();
void setId(Integer id);
}
Loan entity:
package com.demien.springmvcrest.domain;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Entity;
@Entity(name = "LOAN")
public class Loan implements Serializable, IPersistable {
public static enum STATE {
OPEN, CLOSED, EXTENDED
}
private static final long serialVersionUID = 7566184847780781908L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
private Integer id;
@Column(name = "IP_ADDR")
private String ipAddr;
@Column(name = "AMOUNT")
private Float amount;
@Column(name = "RATE")
private Float rate;
@Column(name = "STATE")
private String state;
@Column(name = "DEADLINE")
private Date deadLine;
@Column(name = "PARENT_ID")
private Integer parentId;
@Column(name = "CREATE_DATE")
private Date createDate;
@Column(name = "CHANGE_DATE")
private Date changeDate;
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name= "USER_ID")
private User user;
public Loan() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getIpAddr() {
return ipAddr;
}
public void setIpAddr(String ipAddr) {
this.ipAddr = ipAddr;
}
public Float getAmount() {
return amount;
}
public void setAmount(Float amount) {
this.amount = amount;
}
public Float getRate() {
return rate;
}
public void setRate(Float rate) {
this.rate = rate;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Date getDeadLine() {
return deadLine;
}
public void setDeadLine(Date deadLine) {
this.deadLine = deadLine;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getChangeDate() {
return changeDate;
}
public void setChangeDate(Date changeDate) {
this.changeDate = changeDate;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Loan clone() {
Loan loan = new Loan();
loan.setAmount(new Float(this.getAmount()));
loan.setDeadLine(new Date(this.getDeadLine().getTime()));
loan.setIpAddr(new String(this.getIpAddr()));
if (this.getParentId() != null) {
loan.setParentId(new Integer(this.getParentId()));
}
loan.setRate(new Float(this.getRate()));
loan.setState(new String(this.getState()));
loan.setCreateDate(new Date());
return loan;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Loan)) {
return false;
} else {
Loan loan = (Loan) object;
if (loan.getId().equals(this.getId())
&& loan.getIpAddr().equals(this.getIpAddr())
&& loan.getCreateDate().equals(this.getCreateDate())) {
return true;
}
return false;
}
}
@Override
public String toString() {
return "Loan [id=" + id + ", ipAddr=" + ipAddr + ", amount=" + amount
+ ", rate=" + rate + ", state=" + state + ", deadLine="
+ deadLine + ", parentId=" + parentId + ", createDate="
+ createDate + ", changeDate=" + changeDate + "]";
}
@Override
public int hashCode() {
return getId();
}
}
3. DAO
- Used generic dao, like in my previous posts.
3.1 Interface
package com.demien.springmvcrest.dao;
import java.util.List;
import java.util.Map;
public interface IBaseDAO<T> {
public T get(Integer id);
public T save(T object);
public void update(T object);
public void delete(T object);
public List<T> query(String hsql, Map<String, Object> params);
}
3.2 Implementation
package com.demien.springmvcrest.dao;
import java.util.List;
import java.util.Map;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class BaseDAOImpl<T> implements IBaseDAO<T> {
private SessionFactory sessionFactory;
private Class<T> cl;
public BaseDAOImpl(Class<T> cl, SessionFactory sessionFactory) {
this.cl=cl;
this.sessionFactory = sessionFactory;
}
@Override
public T get(Integer id) {
Session session = sessionFactory.getCurrentSession();
@SuppressWarnings("unchecked")
T element = (T) session.get(cl, id);
return element;
}
@SuppressWarnings("unchecked")
@Override
public T save(T object) {
Session session = sessionFactory.getCurrentSession();
T result=(T)session.save(object);
return result;
}
@Override
public void update(T object) {
Session session = sessionFactory.getCurrentSession();
session.update(object);
}
@Override
public void delete(T object) {
Session session = sessionFactory.getCurrentSession();
session.delete(object);
}
@SuppressWarnings("unchecked")
@Override
public List<T> query(String hsql, Map<String, Object> params) {
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery(hsql);
if (params != null) {
for (String i : params.keySet()) {
query.setParameter(i, params.get(i));
}
}
List<T> result = null;
if ((hsql.toUpperCase().indexOf("DELETE") == -1)
&& (hsql.toUpperCase().indexOf("UPDATE") == -1)
&& (hsql.toUpperCase().indexOf("INSERT") == -1)) {
result = query.list();
} else {
query.executeUpdate();
}
return result;
}
}
4. Service level
The same as in y previous posts: BaseService(interface and implementation) for common actions and "specific" services for "specific" actions related with entities - only one of two will be shown.
4.1 BaseService interface
package com.demien.springmvcrest.service;
import java.util.List;
import com.demien.springmvcrest.dao.IBaseDAO;
public interface IBaseService<T> extends IBaseDAO<T> {
List<T> getAll();
void deleteAll();
}
4.2 BaseService implementation
package com.demien.springmvcrest.service;
import java.util.List;
import java.util.Map;
import org.springframework.transaction.annotation.Transactional;
import com.demien.springmvcrest.dao.IBaseDAO;
public abstract class BaseServiceImpl<T> implements IBaseService<T> {
private IBaseDAO<T> dao;
private Class<T> cl;
public BaseServiceImpl(Class<T> cl) {
this.cl=cl;
}
public void setDao(IBaseDAO<T> dao) {
this.dao=dao;
}
@Transactional
@Override
public T get(Integer id) {
return (T) dao.get(id);
}
@Transactional
@Override
public T save(T object) {
T result=(T)dao.save(object);
return result;
}
@Transactional
@Override
public void update(T object) {
dao.update(object);
}
@Transactional
@Override
public void delete(T object) {
dao.delete(object);
}
@Transactional
@Override
public List<T> query(String hsql, Map<String, Object> params) {
return (List<T>)dao.query(hsql, params);
}
@Transactional
@Override
public List<T> getAll() {
return query("from "+cl.getName(), null);
}
@Transactional
@Override
public void deleteAll() {
query("delete from "+cl.getName(),null);
}
}
4.2 Loan service interface - added specific to Loan entity methods:
package com.demien.springmvcrest.service;
import com.demien.springmvcrest.domain.Loan;
public interface ILoanService extends IBaseService<Loan> {
void extendLoan(Integer id);
String checkLoan(final String ipAddr, final Float amount);
void setCheckHourFrom(final int checkHourFrom);
void setCheckHourTo(final int checkHourTo);
}
4.3 Loan Service implementation
package com.demien.springmvcrest.service;
import static com.demien.springmvcrest.AppConst.CHECK_MAX_AMOUNT;
import static com.demien.springmvcrest.AppConst.CHECK_MAX_IP_DAY_REQUESTS;
import static com.demien.springmvcrest.AppConst.REJECT_REASON_BAD_TIME;
import static com.demien.springmvcrest.AppConst.REJECT_REASON_IP_DAY_ATTEMPTS;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.demien.springmvcrest.AppConst;
import com.demien.springmvcrest.dao.IBaseDAO;
import com.demien.springmvcrest.domain.Loan;
@Service
public class LoanServiceImpl extends BaseServiceImpl<Loan> implements ILoanService {
public final static String QUERY_IP_COUNT="from "+Loan.class.getName()+" where ipAddr=:ipAddr and createDate>=:dateFrom and createDate<=:dateTo";
private static int checkHourFrom;
private static int checkHourTo;
public LoanServiceImpl() {
super(Loan.class);
}
@Autowired
@Qualifier("loanDAOImpl")
public void setDao(final IBaseDAO<Loan> dao) {
super.setDao(dao);
}
public void setCheckHourFrom(final int checkHourFrom) {
LoanServiceImpl.checkHourFrom = checkHourFrom;
}
public void setCheckHourTo(final int checkHourTo) {
LoanServiceImpl.checkHourTo = checkHourTo;
}
@SuppressWarnings("deprecation")
public boolean isTimeInHourInterval(final int from, final int to) {
Date d = new Date();
int hour = d.getHours();
if (hour >= from && hour <= to) {
return true;
} else {
return false;
}
}
@SuppressWarnings("deprecation")
public boolean isDateToday(final Date loanDate) {
Date d = new Date();
if (loanDate.getYear() == d.getYear()
&& loanDate.getMonth() == d.getMonth()
&& loanDate.getDay() == d.getDay()) {
return true;
} else {
return false;
}
}
public int getIpRequestTodayCount(final String ipAddr) {
Map<String,Object> params=new HashMap<String,Object>();
params.put("ipAddr", ipAddr);
Date dateTo=new Date();
Date dateFrom=new Date(dateTo.getTime()-AppConst.DAY_SECONDS);
params.put("dateFrom", dateFrom);
params.put("dateTo", dateTo);
List<Loan> loans=query(QUERY_IP_COUNT, params);
return loans.size();
}
@Transactional
@Override
public String checkLoan(final String ipAddr, final Float amount) {
StringBuilder result = new StringBuilder();
// 1 check by max amount and hours
if (amount >= CHECK_MAX_AMOUNT && isTimeInHourInterval(checkHourFrom, checkHourTo)) {
result.append(REJECT_REASON_BAD_TIME);
}
// 2 check by ip addr
if (getIpRequestTodayCount(ipAddr) >= CHECK_MAX_IP_DAY_REQUESTS) {
result.append(REJECT_REASON_IP_DAY_ATTEMPTS);
}
return result.toString();
}
@Transactional
@Override
public void extendLoan(final Integer id) {
Loan oldLoan=get(id);
Loan newLoan=oldLoan.clone();
oldLoan.setState(Loan.STATE.EXTENDED.toString());
oldLoan.setChangeDate(new Date());
newLoan.setRate(newLoan.getRate()*AppConst.EXTEND_RATE_MUL);
Date d=new Date();
d.setTime(d.getTime()+AppConst.EXTEND_INTERVAL);
newLoan.setDeadLine(d);
newLoan.setParentId(id);
update(oldLoan);
save(newLoan);
}
}
5. Controllers
5.1 BaseController - for common CRUD operations on entities.
package com.demien.springmvcrest.controller;
import java.util.List;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.demien.springmvcrest.domain.IPersistable;
import com.demien.springmvcrest.service.IBaseService;
import com.demien.springmvcrest.utils.JsonHelper;
import com.demien.springmvcrest.utils.JsonHelper.JsonHelperException;
import static com.demien.springmvcrest.AppConst.*;
public class BaseController<T extends IPersistable> {
private IBaseService<T> service;
public static final String SERVICE_HELLO="sayhello";
public static final String SERVICE_GET_ALL="getall";
public static final String SERVICE_GETBYID="getbyid";
public static final String SERVICE_ADD="add";
public static final String SERVICE_UPDATE="update";
public static final String SERVICE_DELETE="delete";
private Class<T> cl;
public BaseController(Class<T> cl) {
this.cl=cl;
}
public void setService(IBaseService<T> service) {
this.service = service;
}
@RequestMapping(method=RequestMethod.GET, value="/sayhello")
@ResponseBody
public String sayHello() {
System.out.println("Executing sayHello");
return HELLO;
}
@RequestMapping(method=RequestMethod.GET, value="/getall")
public @ResponseBody String getAll() throws JsonHelperException {
List<T> list = service.getAll();
String json=JsonHelper.object2json(list);
return json;
}
private T getEntityById(Integer id) {
return service.get(id);
}
@RequestMapping(method=RequestMethod.GET, value="/getbyid")
public @ResponseBody String getEntity(@RequestParam("id") Integer id) throws JsonHelperException {
T result =service.get(id);
String resultJson=JsonHelper.object2json(result);
return resultJson;
}
protected T addEntity(T entity) {
Object o=service.save(entity);
if (o instanceof Integer) {
Integer id=(Integer)o;
entity.setId(id);
}
return entity;
}
@SuppressWarnings("unchecked")
@RequestMapping(method=RequestMethod.POST, value="/add")
public @ResponseBody String addEntity(@RequestBody String json) throws JsonHelperException {
T entity=(T)JsonHelper.Json2Object(json, cl);
if (entity==null) {
throw new RuntimeException("addEnity Error: entity is null");
}
T searchResult = getEntityById(entity.getId());
if (searchResult == null) {
entity=addEntity(entity);
}
String resultJson=JsonHelper.object2json(entity);
return resultJson;
}
protected void updateEntity(T entity) {
service.update(entity);
}
@SuppressWarnings("unchecked")
@RequestMapping(method=RequestMethod.POST, value="/update")
public @ResponseBody String updateEntity(@RequestBody String json) throws JsonHelperException {
T entity=(T)JsonHelper.Json2Object(json, cl);
updateEntity(entity);
String resultJson=JsonHelper.object2json(entity);
return resultJson;
}
protected void deleteEntity(T entity) {
service.delete(entity);
}
@SuppressWarnings("unchecked")
@RequestMapping(method=RequestMethod.POST, value="/delete")
public @ResponseBody void deleteEntity(@RequestBody String json) throws JsonHelperException {
T entity=(T)JsonHelper.Json2Object(json, cl);
deleteEntity(entity);
}
}
5.2. "specific" controller example - Loan Controller.
package com.demien.springmvcrest.controller;
import static com.demien.springmvcrest.AppConst.*;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.demien.springmvcrest.domain.Loan;
import com.demien.springmvcrest.domain.User;
import com.demien.springmvcrest.service.ILoanService;
import com.demien.springmvcrest.service.IUserService;
import com.demien.springmvcrest.utils.JsonHelper;
import com.demien.springmvcrest.utils.JsonHelper.JsonHelperException;
@Controller
@RequestMapping(value = "/loan")
public class LoanController extends BaseController<Loan> {
private ILoanService loanService;
private IUserService userService;
// services
public static final String SERVICE_MAIN = "loan";
public static final String SERVICE_CREATE_LOAN = "createLoan";
public static final String SERVICE_EXTEND_LOAN = "extendLoan";
public LoanController() {
super(Loan.class);
}
@Autowired
public void setLoanService(ILoanService loanService) {
this.loanService = loanService;
super.setService(loanService);
}
@Autowired
public void setUserService(IUserService userService) {
this.userService = userService;
}
@RequestMapping(method=RequestMethod.GET, value="/createLoan")
public @ResponseBody String createLoan(@RequestParam final String amount,
@RequestParam final String date,
@RequestParam final Integer userId, HttpServletRequest request) {
String ipAddr = "";
ipAddr=request.getRemoteAddr();
Loan loan = new Loan();
// checks for input params
StringBuilder error = new StringBuilder();
if (amount == null || amount.length() < 1) {
error.append(ERROR_AMOUNT_IS_NULL);
}
if (date == null || amount.length() < 1) {
error.append(ERROR_DATE_IS_NULL);
}
try {
loan.setAmount(Float.parseFloat(amount));
} catch (Exception e) {
error.append(ERROR_WRONG_AMOUNT_FORMAT);
}
try {
loan.setDeadLine(FORMATTER.parse(date));
} catch (Exception e) {
error.append(ERROR_WRONG_DATE_FORMAT);
}
User user = userService.get(userId);
if (user == null) {
error.append(ERROR_WRONG_USER_ID);
}
// main check procedure
error.append(loanService.checkLoan(ipAddr, loan.getAmount()));
if (error.length() == 0) {
loan.setRate(DEFAULT_RATE);
loan.setIpAddr(ipAddr);
loan.setState(Loan.STATE.OPEN.toString());
loan.setCreateDate(new Date());
loan.setUser(user);
addEntity(loan);
return RESULT_OK;
} else {
return RESULT_REJECTED + error.toString();
}
}
@RequestMapping(method=RequestMethod.GET, value="/extendLoan")
public @ResponseBody String extendloan(@RequestParam("id") final Integer id) throws JsonHelperException {
String json=getEntity(id);
Loan loan=(Loan) JsonHelper.Json2Object(json, Loan.class);
Date now=new Date();
if (loan.getDeadLine().getTime()<now.getTime()) {
return ERROR_EXTEND_DEADLINE_EXCEED;
}
loanService.extendLoan(id);
return RESULT_EXTENDED;
}
}
6. Configuration
6.1. Main config
package com.demien.springmvcrest.config;
import java.util.Properties;
import org.hibernate.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import com.demien.springmvcrest.dao.BaseDAOImpl;
import com.demien.springmvcrest.dao.IBaseDAO;
import com.demien.springmvcrest.domain.Loan;
import com.demien.springmvcrest.domain.User;
@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = "com.demien.springmvcrest")
public class AppConfig {
// ---------- DAO ------------------
@Bean
public DriverManagerDataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
Properties p = new Properties();
dataSource.setConnectionProperties(p);
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:~/test");
dataSource.setUsername("sa");
dataSource.setPassword("sa");
return dataSource;
}
@SuppressWarnings("rawtypes")
@Bean
public LocalSessionFactoryBean sessionFactoryBean() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
Class[] annotatedClasses = new Class[] { User.class, Loan.class };
sessionFactoryBean.setAnnotatedClasses(annotatedClasses);
Properties p = new Properties();
p.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
p.put("hibernate.show_sql", "true");
p.put("hibernate.hbm2ddl.auto", "create");
sessionFactoryBean.setHibernateProperties(p);
return sessionFactoryBean;
}
@Bean
public SessionFactory sessionFactory() {
return sessionFactoryBean().getObject();
}
@Bean
public HibernateTransactionManager txManager() {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory());
return txManager;
}
@Bean
public TransactionAttributeSource annotationTransactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
@Bean
public IBaseDAO<Loan> loanDAOImpl() {
IBaseDAO<Loan> loanDAOImpl = new BaseDAOImpl<Loan>(Loan.class,
sessionFactory());
return loanDAOImpl;
}
@Bean
public IBaseDAO<User> userDAOImpl() {
IBaseDAO<User> userDAOImpl = new BaseDAOImpl<User>(User.class,
sessionFactory());
return userDAOImpl;
}
}
6.2. Configuration for enabling MVC
package com.demien.springmvcrest.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
@EnableWebMvc
@Profile("container")
public class WebMvcConfig extends WebMvcConfigurerAdapter {
}
7. Utilities
7.1 JsonJelper - for converting Object->Json->Object
package com.demien.springmvcrest.utils;
import java.util.List;
import org.codehaus.jackson.map.ObjectMapper;
public class JsonHelper {
private static ObjectMapper mapper = new ObjectMapper();
@SuppressWarnings("rawtypes")
public static Object Json2Object(String json, Class cl) throws JsonHelperException {
return Json2ObjectMain(json, cl, false);
}
@SuppressWarnings("rawtypes")
public static Object Json2ObjectList(String json, Class cl) throws JsonHelperException{
return Json2ObjectMain(json, cl, true);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static Object Json2ObjectMain(String json, Class cl, boolean asList) throws JsonHelperException {
if (json==null || json.length()==0) {
return null;
}
Object result=null;
try {
if (asList) {
result=mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, cl));
} else {
result=mapper.readValue(json, cl);
}
} catch (Exception e) {
throw new JsonHelper.JsonHelperException("Exception in Json2Object. Exception:"+e.getMessage()+". JSON="+json);
}
return result;
}
public static String object2json(Object object) throws JsonHelperException {
String result="";
try {
result=mapper.writeValueAsString(object);
} catch (Exception e){
throw new JsonHelper.JsonHelperException("Exception in object2json. Exception:"+e.getMessage()+".");
}
return result;
}
public static class JsonHelperException extends Exception {
private static final long serialVersionUID = -7568626836534766197L;
public JsonHelperException(String message) {
super(message);
}
}
}
7.2. Object data populator - for tests - fill object fields with random values
package com.demien.springmvcrest.utils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import com.demien.springmvcrest.domain.IPersistable;
public class ObjectDataPopulator {
public static Object populate(final IPersistable instance)
throws Exception {
instance.setId(getRandomInteger());
List<Field> fields = getAllFields(instance);
for (Field eachField : fields) {
eachField.setAccessible(true);
if (eachField.getType().equals(Integer.class)) {
eachField.set(instance, getRandomInteger());
} else if (eachField.getType().equals(String.class)) {
eachField.set(instance, getRandomString());
} else if (eachField.getType().equals(Float.class)) {
eachField.set(instance, getRandomFloat());
} else if (eachField.getType().equals(Date.class)) {
eachField.set(instance, getRandomDate());
} else if (eachField.getType().equals(Set.class)) {
// here should be generators for other standard types like Float, Long....
} else {
// processing aggregated objects
if (eachField.getType().newInstance() instanceof IPersistable) {
//eachField.set(instance, populate((IPersistable) eachField.getType().newInstance()));
}
}
}
return instance;
}
private static Integer getRandomInteger() {
return new Integer((int) (Math.random() * 1000));
}
private static Float getRandomFloat() {
return new Float( Math.random() * 1000+Math.random());
}
private static Date getRandomDate() {
return new Date( new Date().getTime() - (int)(Math.random() * 1000*60*60*24*100) );
}
private static String getRandomString() {
StringBuffer result = new StringBuffer();
String[] letters = new String[] { "A", "B", "C", "D", "E", "F", "G" };
int length = (int) (Math.random() * 15) + 5;
for (int i = 0; i < length; i++) {
int pos = (int) (Math.random() * letters.length);
result.append(letters[pos]);
}
return result.toString();
}
private static List<Field> getAllFields(final Object instance) {
Field[] fields = instance.getClass().getDeclaredFields();
List<Field> result = new ArrayList<Field>();
for (int i = 0; i < fields.length; i++) {
if (!java.lang.reflect.Modifier.isFinal(fields[i].getModifiers())
&& !java.lang.reflect.Modifier.isStatic(fields[i]
.getModifiers())) {
result.add(fields[i]);
}
}
return result;
}
}
7.3. RestTestClient - client for calling rest methods and transform results into proper objects
package com.demien.springmvcrest.utils;
import java.io.BufferedReader;
import org.apache.http.NameValuePair;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import com.demien.springmvcrest.utils.JsonHelper.JsonHelperException;
public class RestTestClient {
public static final int RESPONSE_OK = 200;
public String getResponseData(HttpResponse response)
throws Exception {
StringBuilder result = new StringBuilder();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(
response.getEntity().getContent()));
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
} catch (Exception e) {
throw new Exception("Exception in getResponseData:"
+ e.getMessage() + ".");
}
return result.toString();
}
@SuppressWarnings("rawtypes")
public Object Json2Object(String src, Class cl, boolean asList)
throws JsonHelperException {
Object result = null;
if (asList) {
result = JsonHelper.Json2ObjectList(src, cl);
} else {
result = JsonHelper.Json2Object(src, cl);
}
return result;
}
public HttpResponse sendGet(String url) throws ClientProtocolException,
IOException {
HttpClient client = HttpClientBuilder.create().build();
HttpGet get = new HttpGet(url);
get.setHeader(
"Accept",
"text/html,application/xhtml+xml,application/xml,application/json;q=0.9,*/*;q=0.8");
get.setHeader("Content-Type", "application/json");
HttpResponse response = client.execute(get);
return response;
}
public String sendGetStringResult(String url) throws Exception {
return getResponseData(sendGet(url));
}
@SuppressWarnings("rawtypes")
public Object sendGetObjectResult(String url, Class cl) throws Exception {
return sendGetObjectResult(url, cl, false);
}
@SuppressWarnings("rawtypes")
public Object sendGetObjectResult(String url, Class cl, boolean asList)
throws Exception {
String src = sendGetStringResult(url);
Object result = null;
if (asList) {
result = JsonHelper.Json2ObjectList(src, cl);
} else {
result = JsonHelper.Json2Object(src, cl);
}
return result;
}
public HttpResponse sendPost(String url, List<NameValuePair> postParams, Object object) throws Exception {
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(url);
post.setHeader("Accept",
"text/html,application/xhtml+xml,application/xml, application/json;q=0.9,*/*;q=0.8");
if (postParams != null) {
post.setEntity(new UrlEncodedFormEntity(postParams));
post.setHeader("Content-Type", "application/x-www-form-urlencoded");
}
if (object!=null) {
String json = JsonHelper.object2json(object);
StringEntity input = new StringEntity(json);
input.setContentType("application/json");
post.setEntity(input);
post.setHeader("Content-Type", "application/json");
}
HttpResponse response = client.execute(post);
return response;
}
public HttpResponse sendPost(String url, List<NameValuePair> postParams)
throws Exception {
return sendPost(url, postParams, null);
}
public HttpResponse sendPost(String url, Object object) throws Exception {
return sendPost(url, null, object);
}
public String sendPostStringResult(String url,
List<NameValuePair> postParams) throws IllegalStateException,
IOException, Exception {
return getResponseData(sendPost(url, postParams));
}
@SuppressWarnings("rawtypes")
public Object sendPostObjectResult(String url,
List<NameValuePair> postParams, Class cl)
throws IllegalStateException, IOException, Exception {
return sendPostObjectResult(url, postParams, cl, false);
}
@SuppressWarnings("rawtypes")
public Object sendPostObjectResult(String url,
List<NameValuePair> postParams, Class cl, boolean asArray)
throws IllegalStateException, IOException, Exception {
String src = sendPostStringResult(url, postParams);
return Json2Object(src, cl, asArray);
}
}
7.4. RestTestServer - starting application on embedded jetty server
package com.demien.springmvcrest.utils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import java.io.IOException;
public class RestTestServer {
public static final int TEST_PORT = 8080;
public static final String TEST_CONTEXT = "/";
private static final String CONFIG_LOCATION = "com.demien.springmvcrest.config";
private static final String MAPPING_URL = "/*";
private String mode;
private Server server;
public RestTestServer() {
}
public RestTestServer(final String mode) {
this.mode = mode;
}
public void start() throws Exception {
//LOGGER.debug("Starting server at port :"+ TEST_PORT);
server = new Server(TEST_PORT);
server.setHandler(getServletContextHandler(getContext()));
server.start();
System.out.println("Server started at http://localhost:" + TEST_PORT + TEST_CONTEXT);
//LOGGER.info("Server started at http://localhost:" + TEST_PORT + TEST_CONTEXT);
if (mode == null || !mode.equals("JUNIT")) {
server.join();
}
}
public void stop() throws Exception {
if (server != null) {
server.stop();
server.join();
server.destroy();
server = null;
}
}
private ServletContextHandler getServletContextHandler(WebApplicationContext context) throws IOException {
ServletContextHandler contextHandler = new ServletContextHandler();
contextHandler.setErrorHandler(null);
contextHandler.setContextPath(TEST_CONTEXT);
contextHandler.addServlet(new ServletHolder(new DispatcherServlet(context)), MAPPING_URL);
contextHandler.addEventListener(new ContextLoaderListener(context));
contextHandler.setResourceBase(new ClassPathResource("webapp").getURI().toString());
return contextHandler;
}
private WebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
//context.register(AppConfig.class);
context.setConfigLocation(CONFIG_LOCATION);
return context;
}
}
8. Unit tests - there are a lot of tests in application - only few of them will be shown.
8.1. BaseDAOTest
package com.demien.springmvcrest.dao;
import java.io.Serializable;
import junit.framework.Assert;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Before;
import org.junit.Test;
import com.demien.springmvcrest.dao.BaseDAOImpl;
import static org.mockito.Mockito.*;
public class BaseDAOTest {
BaseDAOImpl<TestClass> dao;
SessionFactory sessionFactory;
Session session;
TestClass testObject;
private class TestClass implements Serializable {
private static final long serialVersionUID = -8877252618686760948L;
}
@Before
public void init() {
testObject=new TestClass();
sessionFactory=mock(SessionFactory.class);
session=mock(Session.class);
when(sessionFactory.getCurrentSession()).thenReturn(session);
dao=new BaseDAOImpl<TestClass>(TestClass.class, sessionFactory);
}
@Test
public void getTest() {
Integer id=1;
when(session.get(TestClass.class, id)).thenReturn(testObject);
Object result=dao.get(id);
Assert.assertEquals(testObject, result);
}
@Test
public void saveTest() {
TestClass newObject=new TestClass();
when(session.save(testObject)).thenReturn(newObject);
Object result=dao.save(testObject);
Assert.assertEquals(newObject, result);
}
@Test
public void updateTest() {
dao.update(testObject);
verify(session).update(testObject);
}
@Test
public void deleteTest() {
dao.delete(testObject);
verify(session).delete(testObject);
}
@Test
public void queryTest() {
Query query=mock(Query.class);
when(session.createQuery(anyString())).thenReturn(query);
//1
dao.query("select * from dual", null);
verify(query).list();
//2
dao.query("delete from dual", null);
verify(query).executeUpdate();
}
}
8.2. LoanController test
package com.demien.springmvcrest.rest;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import com.demien.springmvcrest.service.ILoanService;
import com.demien.springmvcrest.service.IUserService;
import com.demien.springmvcrest.service.LoanServiceImplTest;
import com.demien.springmvcrest.utils.JsonHelper.JsonHelperException;
import com.demien.springmvcrest.utils.ObjectDataPopulator;
import com.demien.springmvcrest.AppConst;
import com.demien.springmvcrest.controller.LoanController;
import com.demien.springmvcrest.domain.Loan;
import com.demien.springmvcrest.domain.User;
public class LoanControllerTest extends BaseControllerTest<Loan> {
private static ILoanService loanService;
private static IUserService userService;
private LoanController controller;
public LoanControllerTest() {
super(Loan.class);
}
@Before
public void init() {
loanService=mock(ILoanService.class);
userService=mock(IUserService.class);
controller=new LoanController();
controller.setLoanService(loanService);
controller.setUserService(userService);
}
@Test
public void createLoanTest () throws Exception {
String ipAddr="127.0.0.1";
HttpServletRequest request=mock(HttpServletRequest.class);
when(request.getRemoteAddr()).thenReturn(ipAddr);
User user=new User();
ObjectDataPopulator.populate(user);
Loan loan=new Loan();
ObjectDataPopulator.populate(loan);
when(userService.get(user.getId())).thenReturn(user);
ArgumentCaptor<Loan> captor = ArgumentCaptor.forClass(Loan.class);
String dateStr=AppConst.FORMATTER.format(loan.getDeadLine());
when(loanService.checkLoan(ipAddr, loan.getAmount())).thenReturn("");
controller.createLoan(loan.getAmount().toString(), dateStr, user.getId(), request);
verify(userService).get(user.getId());
verify(loanService).save(captor.capture());
assertEquals(loan.getAmount(), captor.getValue().getAmount());
assertEquals(dateStr, AppConst.FORMATTER.format(captor.getValue().getDeadLine()) );
assertEquals(user.getId(), captor.getValue().getUser().getId());
}
@Test
public void extendLoanTest() throws JsonHelperException {
Integer id=666;
Date d=new Date();
Loan loan=LoanServiceImplTest.getTestLoan("", new Date());
loan.setDeadLine(new Date(d.getTime()+AppConst.DAY_SECONDS));
when(loanService.get(id)).thenReturn(loan);
controller.extendloan(id);
verify(loanService).extendLoan(id);
}
}
9. Integration tests
9.1. Base Integration test - test for BaseController
package com.demien.springmvcrest.integration.test;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.List;
import org.apache.http.HttpResponse;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import com.demien.springmvcrest.controller.BaseController;
import com.demien.springmvcrest.utils.ObjectDataPopulator;
import com.demien.springmvcrest.utils.RestTestClient;
import com.demien.springmvcrest.utils.RestTestServer;
import com.demien.springmvcrest.AppConst;
import com.demien.springmvcrest.domain.IPersistable;
public abstract class BaseIT<T extends IPersistable> {
protected static RestTestServer server = new RestTestServer("JUNIT");
protected static RestTestClient client = new RestTestClient();
protected final String BASE_URL = "http://localhost:"
+ RestTestServer.TEST_PORT + RestTestServer.TEST_CONTEXT
//+ "/services/rest/"
;
protected final String SERVICE_URL;
private Class<T> cl;
public BaseIT(String sevicePrexif, Class<T> cl) {
SERVICE_URL = BASE_URL + sevicePrexif + "/";
this.cl=cl;
}
@BeforeClass
public static void init() throws Exception {
server.start();
}
@AfterClass
public static void finish() throws Exception {
server.stop();
}
private T getTestEntity() throws Exception {
T entity = cl.newInstance();
ObjectDataPopulator.populate(entity);
return entity;
}
@Test
public void sayHello() throws Exception {
String value = client.sendGetStringResult(SERVICE_URL
+ BaseController.SERVICE_HELLO);
System.out.println("value=" + value);
assertEquals(AppConst.HELLO, value);
}
@SuppressWarnings("unchecked")
private T getById(Integer id) throws Exception {
T result = (T) client.sendGetObjectResult(SERVICE_URL
+ BaseController.SERVICE_GETBYID + "?id=" + id, cl);
return result;
}
private void addEntity(T entity) throws Exception {
HttpResponse response = client.sendPost(SERVICE_URL
+ BaseController.SERVICE_ADD, entity);
int responseCode = response.getStatusLine().getStatusCode();
assertEquals(RestTestClient.RESPONSE_OK, responseCode);
}
private void updateEntity(T entity) throws Exception {
HttpResponse response = client.sendPost(SERVICE_URL
+ BaseController.SERVICE_UPDATE, entity);
int responseCode = response.getStatusLine().getStatusCode();
assertEquals(RestTestClient.RESPONSE_OK, responseCode);
}
private void deleteEntity(T entity) throws Exception {
HttpResponse response = client.sendPost(SERVICE_URL
+ BaseController.SERVICE_DELETE, entity);
int responseCode = response.getStatusLine().getStatusCode();
assertEquals(RestTestClient.RESPONSE_OK, responseCode);
}
@SuppressWarnings({ "unchecked" })
private List<T> getEntityList() throws IllegalStateException, IOException,
Exception {
List<T> result = (List<T>) client.sendGetObjectResult(SERVICE_URL
+ BaseController.SERVICE_GET_ALL, cl, true);
return result;
}
@Test
public void addEntityTest() throws Exception {
int cnt=getEntityList().size();
T testEntity = getTestEntity();
addEntity(testEntity);
assertEquals(cnt+1, getEntityList().size());
}
@Test
public void updateEntityTest() throws Exception {
T testEntity = getTestEntity();
addEntity(testEntity);
testEntity=getEntityList().get(0);
Integer id = testEntity.getId();
T forUpdate = getTestEntity();
forUpdate.setId(id);
updateEntity(forUpdate);
T searchEntity = getById(id);
assertEquals(forUpdate, searchEntity);
}
@Test
public void deleteEntityTest() throws Exception {
T testEntity = getTestEntity();
addEntity(testEntity);
Integer id = getEntityList().get(0).getId();
testEntity.setId(id);
deleteEntity(testEntity);
T searchEntity = getById(id);
assertTrue(searchEntity==null);
}
}
9.2. Loan integration test- for Loan Controller
package com.demien.springmvcrest.integration.test;
import static com.demien.springmvcrest.AppConst.*;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import java.util.Date;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.web.WebAppConfiguration;
import com.demien.springmvcrest.controller.BaseController;
import com.demien.springmvcrest.controller.LoanController;
import com.demien.springmvcrest.service.ILoanService;
import com.demien.springmvcrest.service.IUserService;
import com.demien.springmvcrest.utils.ObjectDataPopulator;
import com.demien.springmvcrest.config.AppConfig;
import com.demien.springmvcrest.config.WebMvcConfig;
import com.demien.springmvcrest.domain.Loan;
import com.demien.springmvcrest.domain.User;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class,WebMvcConfig.class}, loader = AnnotationConfigContextLoader.class)
@WebAppConfiguration
public class LoanIT extends BaseIT<Loan> {
@Autowired
private ILoanService loanService;
@Autowired
private IUserService userService;
@Autowired
private LoanController loanController;
private final int DAY_SECONDS=1000*60*60*24;
public LoanIT() {
super(LoanController.SERVICE_MAIN, Loan.class);
}
@Before
public void clear() throws Exception {
loanService.deleteAll();
userService.deleteAll();
// create test user
User user=new User();
ObjectDataPopulator.populate(user);
userService.save(user);
}
private Integer getTestUserId() {
User user=userService.getAll().get(0);
return user.getId();
}
@SuppressWarnings("unchecked")
private List<Loan> getAllLoans() throws Exception {
List<Loan> loans = (List<Loan>) client.sendGetObjectResult(SERVICE_URL
+ BaseController.SERVICE_GET_ALL, Loan.class, true);
assertNotNull(loans);
return loans;
}
private String createLoan(final Float amount, final Date date, final Integer userId) throws Exception {
String result=client.sendGetStringResult(SERVICE_URL+LoanController.SERVICE_CREATE_LOAN+"?amount="+amount+"&date="+FORMATTER.format(date)+"&userId="+userId);
assertNotNull(result);
return result;
}
@Test
public void loanCreationSuccessfullTest() throws Exception {
List<Loan> loans =getAllLoans();
int cnt=loans.size();
assertEquals(0, cnt);
// create
String result=createLoan(1f, new Date(), getTestUserId());
assertEquals(result, RESULT_OK);
loans =getAllLoans();
assertEquals(1, loans.size());
//check for user
Loan loan=loans.get(0);
assertEquals(getTestUserId(), loan.getUser().getId());
}
@Test
public void loanCreationRejectedByMaxIpRequestTest() throws Exception {
// create 3 requests from one ip
for (int i=0; i<CHECK_MAX_IP_DAY_REQUESTS; i++) {
String result=createLoan(1f, new Date(), getTestUserId());
assertEquals(result, RESULT_OK);
}
// next should fail
String result=createLoan(1f, new Date(), -666);
assertTrue(result.contains(RESULT_REJECTED));
assertTrue(result.contains(REJECT_REASON_IP_DAY_ATTEMPTS));
assertTrue(result.contains(ERROR_WRONG_USER_ID));
}
@SuppressWarnings("deprecation")
@Test
public void loanCreationRejectedByBadTimeAndMaxAmountTest() throws Exception {
// set "wrong time"
Date d=new Date();
loanService.setCheckHourFrom(d.getHours()-1);
loanService.setCheckHourTo(d.getHours()+1);
String result=createLoan(CHECK_MAX_AMOUNT+1, new Date(), getTestUserId());
assertTrue(result.contains(RESULT_REJECTED));
assertTrue(result.contains(REJECT_REASON_BAD_TIME));
}
@Test
public void testLoanExtendSuccess() throws Exception {
Date d=new Date();
String result=createLoan(1f, new Date(d.getTime()+DAY_SECONDS ), getTestUserId());
assertEquals(result, RESULT_OK);
List<Loan> loans =getAllLoans();
int cnt=loans.size();
Loan loan=loans.get(0);
result=client.sendGetStringResult(SERVICE_URL+LoanController.SERVICE_EXTEND_LOAN+"?id="+loan.getId());
assertEquals(RESULT_EXTENDED, result);
loans =getAllLoans();
assertEquals(cnt+1, loans.size());
// check for correct ID/PARENT ID
int cnt1=0;
int cnt2=0;
for (Loan eachLoan:loans) {
if (eachLoan.getId().equals(loan.getId())) {
cnt1++;
assertEquals(Loan.STATE.EXTENDED.toString(), eachLoan.getState());
}
if (eachLoan.getParentId()!=null && eachLoan.getParentId().equals(loan.getId())) {
cnt2++;
assertEquals(Loan.STATE.OPEN.toString(), eachLoan.getState());
assertTrue(eachLoan.getRate()>loan.getRate());
assertTrue(eachLoan.getDeadLine().getTime()>loan.getDeadLine().getTime());
}
}
assertEquals(1,cnt1);
assertEquals(1,cnt2);
}
@Test
public void testLoanExtendFail() throws Exception {
int DAY_SECONDS=1000*60*60*24;
Date d=new Date();
String result=createLoan(1f, new Date(d.getTime()-DAY_SECONDS), getTestUserId());
assertEquals(result, RESULT_OK);
Loan loan=getAllLoans().get(0);
result=client.sendGetStringResult(SERVICE_URL+LoanController.SERVICE_EXTEND_LOAN+"?id="+loan.getId());
assertEquals(ERROR_EXTEND_DEADLINE_EXCEED, result);
// check for correct ID/PARENT ID
}
}
10. resume
Commands for lunching application from command line:
mvn test - run unit tests
mvn integration-test - run integration tests
Full source code can be downloaded from
here.