在简单的java 项目中,如果要做单元测试比较方便,利用Junit工具甚至直接写一个main()方法就可以实现。但是到了web项目中,要做单元测试就没那么简单了。笔者往常的开发测试过程往往是这样的,写好一堆的前端后台方法(没有前端,只好自己一起做了),目测过一遍代码逻辑觉得没问题之后就扔到jetty插件中去跑一遍。出现异常后,通过断点调试找到并解决问题,重启一次jetty,重复以上过程。很明显,这样的做法效率是很低的。
最近抽了点时间,学习了一下如果在Java web中进行单元测试。下面首先介绍下如何在项目中简单应用这个技术,随后会尽可能介绍下相关原理。
<properties>
<main.basedir>${project.basedir}main.basedir>
<spring.version>4.2.3.RELEASEspring.version>
<mybatis.version>3.3.0mybatis.version>
<mybatis-spring.version>1.2.3mybatis-spring.version>
<mybatis-generator.version>1.3.2mybatis-generator.version>
<javax.servlet-api>3.1.0javax.servlet-api>
<commons-dbcp.version>1.4commons-dbcp.version>
<mysql-connector-java.version>5.1.35mysql-connector-java.version>
<jstl.version>1.2jstl.version>
<jetty.port>8080jetty.port>
<jackson.codehaus.version>1.9.13jackson.codehaus.version>
<jackson.fasterxml.version>2.6.3jackson.fasterxml.version>
<testng.version>6.8.1testng.version>
<jsonpath.version>0.8.1jsonpath.version>
<jetty.version>8.1.16.v20140903jetty.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>${mybatis.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>${mybatis-spring.version}version>
dependency>
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>${mybatis-generator.version}version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>${javax.servlet-api}version>
dependency>
<dependency>
<groupId>commons-dbcpgroupId>
<artifactId>commons-dbcpartifactId>
<version>${commons-dbcp.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql-connector-java.version}version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>${jstl.version}version>
dependency>
<dependency>
<groupId>org.codehaus.jacksongroupId>
<artifactId>jackson-core-aslartifactId>
<version>${jackson.codehaus.version}version>
dependency>
<dependency>
<groupId>org.codehaus.jacksongroupId>
<artifactId>jackson-mapper-aslartifactId>
<version>${jackson.codehaus.version}version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-coreartifactId>
<version>${jackson.fasterxml.version}version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>${jackson.fasterxml.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.testnggroupId>
<artifactId>testngartifactId>
<version>${testng.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>com.jayway.jsonpathgroupId>
<artifactId>json-pathartifactId>
<version>${jsonpath.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
dependencies>
/**
* MyBatisController.java
*/
import com.del.entity.Blog;
import com.del.service.MyBatisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("mybatis")
public class MyBatisController {
@Autowired
private MyBatisService myBatisService;
@RequestMapping("getAllBlog")
public ModelAndView getAllBlog(){
ModelAndView modelAndView = new ModelAndView("mybatis");
modelAndView.addObject("result", myBatisService.getBlog(new Blog()));
return modelAndView;
}
@RequestMapping("getBlogById")
public ModelAndView getBlogById(@RequestParam Integer id){
ModelAndView modelAndView = new ModelAndView("mybatis");
Blog blog = new Blog();
blog.setId(id);
modelAndView.addObject("result", myBatisService.getBlog(blog));
return modelAndView;
}
@RequestMapping("getBlogByClass")
public ModelAndView getBlogByClass(@RequestBody Blog blog){
ModelAndView modelAndView = new ModelAndView("mybatis");
modelAndView.addObject("result", myBatisService.getBlog(blog));
return modelAndView;
}
@ResponseBody
@RequestMapping(value = "deleteBlogById", method = RequestMethod.POST)
public Map deleteBlogById(@RequestParam Integer id){
Map resultMap = new HashMap();
boolean isSucceed = myBatisService.delete(id);
if (isSucceed){
resultMap.put("status",Boolean.TRUE);
resultMap.put("message","Success!");
}
else {
resultMap.put("status",Boolean.FALSE);
resultMap.put("message","Error!");
}
return resultMap;
}
}
/**
* MyBatisService.java,提供了对数据库中数据进行增删改的操作。细节不做介绍,有疑问请参照别的资料。
*/
import com.del.dao.IBlogDao;
import com.del.entity.Blog;
import com.del.entity.BlogExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class MyBatisService {
@Autowired
private IBlogDao iBlogDao;
public List getBlog(Blog blog){
BlogExample blogExample = new BlogExample();
BlogExample.Criteria criteria = blogExample.createCriteria();
if (blog.getId() != null){
criteria.andIdEqualTo(blog.getId());
}
if (blog.getName() != null){
criteria.andNameEqualTo(blog.getName());
}
if (blog.getSize() != null){
criteria.andSizeEqualTo(blog.getSize());
}
List list = iBlogDao.selectByExample(blogExample);
return list;
}
public boolean insert(Blog blog){
int count = iBlogDao.insert(blog);
return count==1?true:false;
}
public boolean delete(Integer id){
int count = iBlogDao.deleteByPrimaryKey(id);
return count==1?true:false;
}
}
可以看到目标代码很简单,就是监听几个url查找数据库,然后将查询结果通过modal传到jsp,jsp再做相应的展示工作. 大家应该可以很简单看懂.
下面我们就进入正戏,开始做单元测试.
/**
* UnitTest.java
**/
import com.del.entity.Blog;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@ContextConfiguration(locations = {"classpath*:platform-services-servlet.xml",
"classpath*:mybatis-context.xml"})
@WebAppConfiguration
public class UnitTest extends AbstractTestNGSpringContextTests {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@BeforeClass
public void setup(){
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
}
/**
* 简单测试post请求结果
* @throws Exception
*/
@Test(priority = 1)
public void testIndex() throws Exception {
mockMvc.perform(get("/main/index"))
.andExpect(status().isOk())
.andExpect(view().name("main"));
System.out.println("testIndex...");
}
/**
* 测试简单的get请求方法
* @throws Exception
*/
@Test(priority = 2)
public void testSelect() throws Exception {
List result = new ArrayList();
Blog blog1 = new Blog(1,"first",1);
Blog blog2 = new Blog(2,"second",2);
Blog blog3 = new Blog(3,"three",3);
result.add(blog1);
result.add(blog2);
result.add(blog3);
mockMvc.perform(get("/mybatis/getAllBlog"))
.andExpect(status().isOk())
.andExpect(view().name("mybatis"))
/**
* 这里要注意!!要想用例中可以利用andExpect()方法比较对象,目标类必须有合法的equals()方法!
*/
.andExpect(model().attribute("result",result));
System.out.println("testSelect...");
}
/**
* 测试附带请求参数的post请求方法
* @throws Exception
*/
@Test(priority = 3)
public void testGetBlogById() throws Exception {
Blog blog = new Blog(1,"first",1);
List result = new ArrayList();
result.add(blog);
mockMvc.perform(post("/mybatis/getBlogById").param("id", "1"))
.andExpect(status().isOk())
.andExpect(model().attribute("result",result));
System.out.println("testGetBlogById...");
}
/**
* 测试附带复杂对象的post请求方法
* @throws Exception
*/
@Test(priority = 4)
public void testGetBlogByClass() throws Exception {
Blog blog = new Blog();
blog.setId(1);
Blog expectBlog = new Blog(1,"first",1);
List expectResult = new ArrayList();
expectResult.add(expectBlog);
mockMvc.perform(post("/mybatis/getBlogByClass")
.content(asJsonString(blog))
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(model().attribute("result",expectResult));
System.out.println("testGetBlogByClass...");
}
/**
* 测试接收经过jackson转化的json格式返回结果
* @throws Exception
*/
@Test(priority = 5)
public void testDeleteBlogById() throws Exception {
mockMvc.perform(post("/mybatis/deleteBlogById").param("id","3"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.message").value("Success!"));
System.out.println("testDeleteBlogById...");
}
public static String asJsonString(final Object obj) {
try {
final ObjectMapper mapper = new ObjectMapper();
final String jsonContent = mapper.writeValueAsString(obj);
return jsonContent;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
待完善…