1. Intro
In my previous posts I wrote about :- Spring data simple example
- Spring boot simple example
And now, it's time combine them together :)
It will be standard back-end for rest-based WEB application.
For data access(DAO) it will use SpringData, for rest-part/integrationTesting - SpringBoot, for DB - H2 database.
2.Aplication structure
Standart "maven based" application structure:Main sources :
3.Maven project file(pom.xml)
Here just SpringBoot dependencies for web,data,testing and H2 DB dependency.<?xml version="1.0" encoding="UTF-8"?><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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demien</groupId> <artifactId>sboot</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>sboot</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.2.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
4. Domain objects : interace IPersistable
In several part of application, especially in test, I needed to get ID from entity objects, so I created interface for that purpose and all my domain entities had to implement it:package com.demien.sboot.domain; public interface IPersistable<ID> { ID getId(); void setId(ID id); }
4.1 Domain objects : Category
Just regular POJO, with implementation of described above IPersistable interface.package com.demien.sboot.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity(name = "CATEGORY") public class Category implements IPersistable<Long>{ @Id @GeneratedValue @Column(name = "CATEGORY_ID") private Long categoryId; @Column(name="CATEGORY_NAME") private String categoryName; @Column(name = "CATEGORY_DESCRIPTION") private String categoryDescription; @Column(name="PARENT_CATEGORY_ID") private Long parentCategoryId; public Category() {} public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName; } public String getCategoryDescription() { return categoryDescription; } public void setCategoryDescription(String categoryDescription) { this.categoryDescription = categoryDescription; } public Long getParentCategoryId() { return parentCategoryId; } public void setParentCategoryId(Long parentCategoryId) { this.parentCategoryId = parentCategoryId; } @Override public String toString() { return "Category{" + "categoryId=" + categoryId + ", categoryName='" + categoryName + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Category category = (Category) o; if (!categoryId.equals(category.categoryId)) return false; if (!categoryName.equals(category.categoryName)) return false; if (categoryDescription != null ? !categoryDescription.equals(category.categoryDescription) : category.categoryDescription != null) return false; return !(parentCategoryId != null ? !parentCategoryId.equals(category.parentCategoryId) : category.parentCategoryId != null); } @Override public int hashCode() { return categoryName.hashCode(); } @Override public Long getId() { return categoryId; } @Override public void setId(Long id) { setCategoryId(id); } }
4.2 Domain objects : Param
Just regular POJO, with implementation of described above IPersistable interface.package com.demien.sboot.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity(name="PARAM") public class Param implements IPersistable<Long> { @Id @GeneratedValue @Column (name="PARAM_ID") private Long paramId; @Column(name = "PARAM_TYPE") private String ParamType; @Column(name="PARAM_NAME") private String paramName; @Column(name="PARAM_DESCRIPTION") private String paramDescription; @Column(name="PARAM_PROPERTIES") private String paramProperties; public Param() { } @Override public Long getId() { return getParamId(); } @Override public void setId(Long paramId) { setParamId(paramId); } public Long getParamId() { return paramId; } public void setParamId(Long paramId) { this.paramId = paramId; } public String getParamType() { return ParamType; } public void setParamType(String paramType) { ParamType = paramType; } public String getParamName() { return paramName; } public void setParamName(String paramName) { this.paramName = paramName; } public String getParamDescription() { return paramDescription; } public void setParamDescription(String paramDescription) { this.paramDescription = paramDescription; } public String getParamProperties() { return paramProperties; } public void setParamProperties(String paramProperties) { this.paramProperties = paramProperties; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Param param = (Param) o; if (!paramId.equals(param.paramId)) return false; if (!ParamType.equals(param.ParamType)) return false; return paramName.equals(param.paramName); } @Override public int hashCode() { int result = paramId.hashCode(); result = 31 * result + ParamType.hashCode(); result = 31 * result + paramName.hashCode(); return result; } }
5.1 Repositories : CategoryRepository
Details of using Spring data I described in a post which I mentioned at the beginning. I'm just extending JpaRepository interface and adding one more method to it.package com.demien.sboot.repository; import com.demien.sboot.domain.Category; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import java.util.List; @Repository(value = "CategoryRepository") public interface CategoryRepository extends JpaRepository<Category, Long> { @Query("FROM CATEGORY where CATEGORY_NAME like %?1% ") List<Category> findByName(String name); }
5.2 Repositories : ParamRepository
Here, even easier : all I need is a standard methods from JpaRepository :package com.demien.sboot.repository; import com.demien.sboot.domain.Param; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository(value = "ParamRepository") public interface ParamRepository extends JpaRepository<Param, Long> { }
6. Services
By common practice, business logic have to be separated from DB side and from REST side. That is why I(and not only I) have services in application.6.1 Services : AbstractService
Services will have a lot of common operation(save, getAll, getById), so I decided to implement them only once (DRY - Don't Repeat Yourself).package com.demien.sboot.service; import org.springframework.data.jpa.repository.JpaRepository; import java.io.Serializable; import java.util.List; public abstract class AbstractService<T,ID extends Serializable> { private JpaRepository<T,ID> repository; public AbstractService(JpaRepository<T,ID> repository) { this.repository=repository; } public T save(T entity) { T result=repository.save(entity); return result; } public void delete(T entity) { repository.delete(entity); } public T findOne(ID entityId) { T result=repository.findOne(entityId); return result; } public List<T> findAll() { List<T> result= repository.findAll(); return result; } }
6.2 Services : CategoryService
Now, then main operations moved into AbstractService class, we can just extends from it and add one specific for CategoryService method :package com.demien.sboot.service; import com.demien.sboot.domain.Category; import com.demien.sboot.repository.CategoryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Service; import java.util.List; @Servicepublic class CategoryService extends AbstractService<Category, Long> { private CategoryRepository repository; @Autowired public CategoryService(CategoryRepository repository) { super(repository); this.repository=repository; } public List<Category> findByName(String name) { return repository.findByName(name); } }
6.3 Services : ParamService
Here - we don't have specific methods, so extending will be enough for us :package com.demien.sboot.service; import com.demien.sboot.domain.Param; import com.demien.sboot.repository.ParamRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Servicepublic class ParamService extends AbstractService<Param, Long> { private ParamRepository paramRepository; @Autowired public ParamService(ParamRepository paramRepository) { super(paramRepository); this.paramRepository=paramRepository; } }
7. Controllers
Controllers - interface of our application - they will provide rest services(URLs) for client(HTML5 based UI).7.1 Controllers : AbstractController
Just like for Services, all common logic were separated into Parent class.Here used SpringBoot for annotating methods which will be used as rest-services. Business logic are in service-classes, so every controller has corresponding service object.
package com.demien.sboot.rest; import com.demien.sboot.service.AbstractService; import org.springframework.web.bind.annotation.*; import java.io.Serializable; import java.util.List; public abstract class AbstractController<T, ID extends Serializable> { private AbstractService<T,ID> service; public AbstractController(AbstractService<T,ID> service) { this.service=service; } @RequestMapping("/hello") public String sayHello() { return "Hello from controller:"+ this.getClass(); } @RequestMapping(value = "save", method = RequestMethod.POST) public @ResponseBody T save(@RequestBody T entity) { T result=service.save(entity); return result; } @RequestMapping(value="findOne/{entityId}", method = RequestMethod.GET) public T findOne(@PathVariable ID entityId) { return service.findOne(entityId); } @RequestMapping(value="findAll", method = RequestMethod.GET) public List<T> findAll() { return service.findAll(); } @RequestMapping(value = "delete", method = RequestMethod.POST) public @ResponseBody void delete(@RequestBody T entity) { service.delete(entity); } }
7.2 Controllers : CategoryController
The same as in previous steps : extending and adding one method.package com.demien.sboot.rest; import com.demien.sboot.domain.Category; import com.demien.sboot.service.AbstractService; import com.demien.sboot.service.CategoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController@RequestMapping("/category") public class CategoryController extends AbstractController<Category, Long> { private CategoryService service; @Autowired public CategoryController(CategoryService service) { super(service); this.service=service; } @RequestMapping(value="findByName/{name}", method = RequestMethod.GET) public List<Category> findByName(@PathVariable String name) { return service.findByName(name); } }
7.3 Controllers : ParamController
Just extending base controller class:package com.demien.sboot.rest; import com.demien.sboot.domain.Param; import com.demien.sboot.service.ParamService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController@RequestMapping("/param") public class ParamController extends AbstractController<Param, Long> { @Autowired public ParamController(ParamService service) { super(service); } }
8. DB properties file
DB connection properties are located in resources/application.properties file:#DB properties:db.driver=org.h2.Driverdb.url=jdbc:h2:~/testdb.username=sadb.password=sa #Hibernate Configuration:db.hibernate.dialect=org.hibernate.dialect.H2Dialectdb.hibernate.show_sql=falsedb.entitymanager.packages.to.scan=com.demien.sboot.domaindb.hibernate.hbm2ddl.auto = create
9. DB Config class
Now we can create Spring Configuration file which will be based on resources/application.properties file :package com.demien.sboot; import org.hibernate.ejb.HibernatePersistence; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Properties; @Configuration@EnableTransactionManagement@ComponentScan("com.demien.sdata") @PropertySource("classpath:application.properties") @EnableJpaRepositories("com.demien.sboot.repository") public class DBConfig { private static final String PROP_DATABASE_DRIVER = "db.driver"; private static final String PROP_DATABASE_PASSWORD = "db.password"; private static final String PROP_DATABASE_URL = "db.url"; private static final String PROP_DATABASE_USERNAME = "db.username"; private static final String PROP_HIBERNATE_DIALECT = "db.hibernate.dialect"; private static final String PROP_HIBERNATE_SHOW_SQL = "db.hibernate.show_sql"; private static final String PROP_ENTITYMANAGER_PACKAGES_TO_SCAN = "db.entitymanager.packages.to.scan"; private static final String PROP_HIBERNATE_HBM2DDL_AUTO = "db.hibernate.hbm2ddl.auto"; @Resourceprivate Environment env; @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getRequiredProperty(PROP_DATABASE_DRIVER)); dataSource.setUrl(env.getRequiredProperty(PROP_DATABASE_URL)); dataSource.setUsername(env.getRequiredProperty(PROP_DATABASE_USERNAME)); dataSource.setPassword(env.getRequiredProperty(PROP_DATABASE_PASSWORD)); return dataSource; } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); entityManagerFactoryBean.setDataSource(dataSource()); entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class); entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN)); entityManagerFactoryBean.setJpaProperties(getHibernateProperties()); return entityManagerFactoryBean; } @Bean public JpaTransactionManager transactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); return transactionManager; } private Properties getHibernateProperties() { Properties properties = new Properties(); properties.put("hibernate.dialect", env.getRequiredProperty(PROP_HIBERNATE_DIALECT)); properties.put("hibernate.show_sql", env.getRequiredProperty(PROP_HIBERNATE_SHOW_SQL)); properties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty(PROP_HIBERNATE_HBM2DDL_AUTO)); return properties; } }
10. Application config class
We're almost done! All we need now is to start our application using main class :package com.demien.sboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplicationpublic class App { public static void main(String[] args) { SpringApplication.run(new Class<?>[]{App.class, DBConfig.class}, args); } }
Because of using SpringBoot we don't need Tomcat/Jetty - we just have to run main class. Just after starting we can open browser and point into test services. For ParamContoller we can open:
http://localhost:8080/param/hello
Browser will show :
Hello from controller:class com.demien.sboot.rest.ParamController
11. Testing
We are done with the deveopment, but how can webe sure that our application is working as expected ? Of cource, we have to test it !11.1 Testing : ObjectPopulator
First of all, let's crate ObjectPopulator for filling empty objects with values.package com.demien.sboot.util; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; public class ObjectPopulator { public static Object populate(final Object instance) throws Exception { 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(Long.class)) { eachField.set(instance, getRandomLong()); } 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 { //throw new RuntimeException("Field "+ eachField.getName()+" was not populated!"); } } return instance; } private static Integer getRandomInteger() { return new Integer((int) (Math.random() * 1000)); } private static Long getRandomLong() { return new Long((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; } }
11.2 Testing : AbstractIT
As always, I created base abstract class for testing common methods. For that I have 4 methods annotated with @Test. These methods will be inherited by child classes and they will use them for testing THEIR methods of rest controller.package com.demien.sboot.it; import com.demien.sboot.App; import com.demien.sboot.DBConfig; import com.demien.sboot.domain.IPersistable; import com.demien.sboot.rest.AbstractController; import com.demien.sboot.util.ObjectPopulator; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.http.MockHttpInputMessage; import org.springframework.mock.http.MockHttpOutputMessage; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.context.WebApplicationContext; import java.io.IOException; import java.io.Serializable; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = {App.class, DBConfig.class}) @WebAppConfigurationpublic abstract class AbstractIT<T extends IPersistable<ID>, ID extends Serializable> { private AbstractController<T,ID> controller; private JpaRepository<T,ID> repository; private String urlPrefix; private Class<T> cl; protected MockMvc mockMvc; private HttpMessageConverter jsonConverter; @Autowired private WebApplicationContext webApplicationContext; private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); @Autowired void setConverters(HttpMessageConverter<?>[] converters) { this.jsonConverter = Arrays.asList(converters).stream().filter( hmc -> hmc instanceof MappingJackson2HttpMessageConverter).findAny().get(); Assert.assertNotNull("the JSON message converter must not be null", this.jsonConverter); } public void setController(AbstractController<T,ID> controller) { this.controller=controller; } public void setRepository(JpaRepository<T,ID> repository) { this.repository=repository; } public AbstractIT(String urlPrefix, Class<T> cl) { this.urlPrefix=urlPrefix; this.cl=cl; } protected String jsonToObject(Object o) throws IOException { MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage(); this.jsonConverter.write(o, MediaType.APPLICATION_JSON, mockHttpOutputMessage); return mockHttpOutputMessage.getBodyAsString(); } protected Object jsonToObject(String json) throws IOException { Object result = null; if (json!=null && json.substring(0,1).equals("[")) { result=new ArrayList<T>(); String jsonTrimmed=json.substring(2,json.length()-2); String[] valArray=jsonTrimmed.split("\\},\\{"); for (String val:valArray) { T element=(T)jsonToObject("{"+val+"}"); ((List)result).add(element); } } else { MockHttpInputMessage mockHttpInputMessage = new MockHttpInputMessage(json.getBytes()); try { result = this.jsonConverter.read(cl, mockHttpInputMessage); } catch (Exception e) { e.printStackTrace(); } if (result == null) { mockHttpInputMessage = new MockHttpInputMessage(json.getBytes()); result = this.jsonConverter.read(ArrayList.class, mockHttpInputMessage); } } return result; } protected T getTestEntity() { T result= null; try { result = cl.newInstance(); ObjectPopulator.populate(result); } catch (Exception e) { e.printStackTrace(); } return result; } @Before public void init() { this.mockMvc = webAppContextSetup(webApplicationContext).build(); } public String templateGetRequest(String url) throws Exception { String result=mockMvc.perform(get(url) .contentType(contentType)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString() ; return result; } public String templatePostRequest(String url, String json) throws Exception { String result=mockMvc.perform(post(url) .content(json) .contentType(contentType)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString() ; return result; } public T callSave(T entity) throws Exception { String json= templatePostRequest("/" + urlPrefix + "/save/", jsonToObject(entity)); return (T)jsonToObject(json); } public T callFindOne(ID entityId) throws Exception { String json= templateGetRequest("/" + urlPrefix + "/findOne/" + entityId.toString()); return (T)jsonToObject(json); } public List<T> callFindAll() throws Exception { String json= templateGetRequest("/" + urlPrefix + "/findAll/"); return (List<T>)jsonToObject(json); } public void callDelete(T entity) throws Exception { templatePostRequest("/" + urlPrefix + "/delete/", jsonToObject(entity)); } @Test public void saveTestForNewElement() throws Exception { T testEntity=getTestEntity(); int countBefore=repository.findAll().size(); testEntity= callSave(testEntity); int countAfter=repository.findAll().size(); Assert.assertEquals("count AFTER ADD should be count BEFORE +1 ", countAfter, countBefore+1); Assert.assertTrue("test element should be present in repository", repository.findAll().contains(testEntity)); } @Test public void saveTestForExistingElement() throws Exception { T testEntity=getTestEntity(); testEntity=repository.save(testEntity); T updatedEntity=getTestEntity(); updatedEntity.setId(testEntity.getId()); updatedEntity=callSave(updatedEntity); Assert.assertTrue("old element should not be present in repository", !repository.findAll().contains(testEntity)); Assert.assertTrue("updated element should be present in repository", repository.findAll().contains(updatedEntity)); } @Test public void findAllTest() throws Exception { T testEntity=getTestEntity(); testEntity=repository.save(testEntity); List<T> all= callFindAll(); Assert.assertTrue("test element should be present in result list", all.contains(testEntity)); } @Test public void getByIdTest() throws Exception { T testEntity=getTestEntity(); testEntity=repository.save(testEntity); ID id=testEntity.getId(); T returnEntity=callFindOne(id); Assert.assertTrue("return element should be the same as original element", testEntity.equals(returnEntity)); } @Test public void deleteTest() throws Exception { T testEntity=getTestEntity(); testEntity=repository.save(testEntity); List<T> all= repository.findAll(); Assert.assertTrue("repository should contain SAVED element", all.contains(testEntity)); callDelete(testEntity); all= repository.findAll(); Assert.assertTrue("repository should NOT contain DELETED element", !all.contains(testEntity)); } }
11.3 Testing : CategoryIT
Testing of all base methods like category.save, category.findById, category.findAll and category.delete will be inherited, so we just need to implement only one method - findByName:package com.demien.sboot.it; import com.demien.sboot.domain.Category; import com.demien.sboot.repository.CategoryRepository; import com.demien.sboot.rest.CategoryController; import junit.framework.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; public class CategoryIT extends AbstractIT<Category, Long> { private CategoryController controller; private CategoryRepository repository; private final static String CATEGORY_URL_PREFIX ="category"; @Autowired public void setCategoryController(CategoryController controller) { this.controller=controller; super.setController(controller); } @Autowired public void setCategoryRepository(CategoryRepository repository) { this.repository=repository; super.setRepository(repository); } public CategoryIT() { super(CATEGORY_URL_PREFIX, Category.class); } @Test public void findByNameTest() throws Exception { Category entity=getTestEntity(); entity.setCategoryName("Hello world!"); repository.save(entity); String json= templateGetRequest("/" + CATEGORY_URL_PREFIX + "/findByName/world"); List<Category> returned=(List<Category>)jsonToObject(json); Assert.assertTrue("", returned.get(0).getCategoryName().equals("Hello world!")); } }
11.4 Testing : ParamIT
For testing ParamController - we just need to extend from base class :package com.demien.sboot.it; import com.demien.sboot.domain.Param; import com.demien.sboot.repository.ParamRepository; import com.demien.sboot.rest.ParamController; import org.springframework.beans.factory.annotation.Autowired; public class ParamIT extends AbstractIT<Param, Long> { private ParamController controller; private ParamRepository repository; public ParamIT() { super("param", Param.class); } @Autowired public void setController(ParamController controller) { this.controller = controller; super.setController(controller); } @Autowired public void setRepository(ParamRepository repository) { this.repository = repository; super.setRepository(repository); } }
12. The End
As a result we have application with "db part" provided by SprindData + and "rest part" provided by SpringBoot. Full source code can be downloaded from here.
p.s.
related posts :