创建单元测试
在IDEA里,如果要对某个类创建单元测试,只需要在代码区域激活选中该类的文件,然后按下快捷键:CTRL + SHIFT + T,则会弹出一个菜单,选择后根据向导就可以自动创建单元测试类:
- Testing library: 默认JUnit4
- Class name: 默认在原类添加Test后缀
- Destination package: 和原类相同
- Generate: 可以勾选@Before和@After,用来在测试开始前和结束后做一些操作。
- Member: 显示原类的所有成员函数,基本上全勾选上。
点击确定后,会自动在test\java下生成测试类,例如CourseServiceImplTest:
package com.example.dao.service;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
public class CourseServiceImplTest {
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void deleteByIds() {
}
@Test
public void deleteByIds1() {
}
@Test
public void queryList() {
}
@Test
public void queryList1() {
}
}
测试功能类
如果仅仅是测试类的某些功能,又不需要涉及到网络业务相关的,可以在单元测试类里直接使用该类并调用该类的功能函数,然后对结果进行Assert验证,如果运行单元测试时验证不通过,会在日志中输出不通过的源码位置,进而就可以知道是哪个函数功能测试没有通过了。
可以看一个例子:
import com.example.dao.model.Course;
import com.example.dao.service.CourseService;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class CourseServiceTest {
@Autowired
private CourseService courseService;
@Test
public void getCourse() {
// 测试插入,插入成功的话返回 1
int n = courseService.save(new Course(1040L, "Spring boot Demo", "https://www.spring.com"));
Assert.assertThat(n, CoreMatchers.equalTo(1));
// 测试查询
Course course = courseService.selectByKey(1001);
Assert.assertThat(course.getAuthor(), CoreMatchers.equalTo("spring"));
// 测试删除
//courseService.delete();
}
}
该测试单元主要是对服务类CourseService进行测试,主要测试了插入和查询操作,通过对返回结果的验证来判断功能是否OK,这里使用了断言函数Assert.assertThat来进行验证,该断言的常见用法整理到最后了,可以参考。
测试网络业务相关
MockMvc
上面对单纯功能的测试比较简单,但是如果涉及到网络请求的业务测试,搭建一套网络环境就比较麻烦,如何更简单地进行测试呢?这时候就要用到MockMvc了,可以不用启动工程就可以测试业务逻辑。
MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。
package com.example;
import com.example.dao.model.User;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringRunner.class)
@SpringBootTest
public class CourseControllerTest {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private WebApplicationContext webContext;
private MockMvc mvc = null;
private MockHttpSession session = null;
@Before
public void setupMockMvc() {
logger.info("@Before...");
mvc = MockMvcBuilders.webAppContextSetup(webContext).build();
session = new MockHttpSession();
User user = new User("root", "root");
session.setAttribute("user", user);
}
@After
public void after() {
logger.info("@After...");
}
/**
* 新增教程测试用例
*
* @throws Exception
*/
@Test
public void addCourse() throws Exception {
String json = "{\"author\":\"HAHAHAA\",\"title\":\"Spring\",\"url\":\"http://www.spring.com\"}";
mvc.perform(MockMvcRequestBuilders.post("/course/add")
.accept(MediaType.APPLICATION_JSON_UTF8)
.content(json.getBytes()) //传json参数
.session(session)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
/**
* 获取教程测试用例
*
* @throws Exception
*/
@Test
public void queryCourse() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/course/resource/1001")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.session(session)
)
.andExpect(MockMvcResultMatchers.status().isOk())
// .andExpect(MockMvcResultMatchers.jsonPath("$.author").value("course author name"))
// .andExpect(MockMvcResultMatchers.jsonPath("$.title").value("course tile name"))
.andDo(MockMvcResultHandlers.print());
}
/**
* 修改教程测试用例
*
* @throws Exception
*/
@Test
public void updateCourse() throws Exception {
String json = "{\"author\":\"测试修改\",\"id\":1031,\"title\":\"Spring教程\",\"url\":\"http://www.spring.com\"}";
mvc.perform(MockMvcRequestBuilders.post("/course/update")
.accept(MediaType.APPLICATION_JSON_UTF8)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(json.getBytes())//传json参数
.session(session)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
/**
* 删除教程测试用例
*
* @throws Exception
*/
@Test
public void deleteCourse() throws Exception {
String json = "[1031]";
mvc.perform(MockMvcRequestBuilders.post("/course/delete")
.accept(MediaType.APPLICATION_JSON_UTF8)
.content(json.getBytes())//传json参数
.session(session)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
}
postman
用户在开发或者调试网络程序或者是网页B/S模式的程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的Firebug等网页调试工具。今天给大家介绍的这款网页调试工具不仅可以调试简单的css、html、脚本等简单的网页基本信息,它还可以发送几乎所有类型的HTTP请求!Postman在发送网络HTTP请求方面可以说是Chrome插件类产品中的代表产品之一。
插件安装
chrome网上应用商店安装,貌似已经搜索不到了。
软件安装
https://www.getpostman.com/apps,下载安装。Postman提供了独立的安装包,不再依赖于Chrome浏览器了。同时支持MAC、Windows和Linux,推荐使用这种方式安装。
测试回滚
有时不希望测试时对数据库里产生垃圾数据,那么可以在测试时回滚下数据,这需要数据库支持回滚,例如MySQL的存储引擎为InnoDB时是支持回滚的,如果不是可以修改一下,具体可以参考:springboot极简教程010-数据库准备
然后在测试函数上添加注解:@Transactional和@Rollback(true)即可,例如:
@RunWith(SpringRunner.class)
@SpringBootTest
public class CourseServiceTest {
@Autowired
private CourseService courseService;
@Test
@Transactional
@Rollback(true)
public void getCourse() {
// 测试插入,插入成功的话返回 1
int n = courseService.save(new Course(1040L, "Spring boot Demo", "https://www.spring.com"));
Assert.assertThat(n, CoreMatchers.equalTo(1));
// 测试查询
Course course = courseService.selectByKey(1001);
Assert.assertThat(course.getAuthor(), CoreMatchers.equalTo("spring"));
// 测试删除
//courseService.delete();
}
}
Assert.assertThat
// 相当于Object的equals方法
Assert.assertThat(a, CoreMatchers.equalTo(b));
// 对象不相等
assertThat(s, not(equalTo("developer")));
// 忽略大小写比较两个字符串是否相等
assertThat(testedString, equalToIgnoringCase(expectedString));
// 忽略头尾任意个空白字符比较两个字符串是否相等(字符串中的空白字符不忽略)
assertThat(testedString, equalToIgnoringWhiteSpace(expectedString);
// 字符串中是否包含某个子串
assertThat(s, not(containsString("Works")));
assertThat(s, anyOf(containsString("developer"), containsString("Works")));
// 字符串以某个前缀开头
assertThat(s, startsWith(prefix));
// 字符串以某个后缀结尾
assertThat(s, endsWith(suffix));
// 判断对象是否为null
assertThat(object, nullValue());
// 判断对象是否不为null
assertThat(object, notNullValue());
// is表达式
assertThat(testedString, is(equalTo(expectedValue)));
assertThat(testedValue, is(expectedValue));
// 断言对象是某类的实例
assertThat(testedObject, is(Cheddar.class));
//
assertThat(testedString, not(expectedString));
// allOf断言符合所有条件,相当于“与”(&&)
assertThat(testedNumber, allOf( greaterThan(8), lessThan(16) ) );
// anyOf断言符合条件之一,相当于“或”(||)
assertThat(testedNumber, anyOf( greaterThan(16), lessThan(8) ) );
//数值相关
// closeTo断言浮点型数testedDouble在20.0+-0.5范围之内
assertThat(testedDouble, closeTo( 20.0, 0.5 ));
// greaterThan断言数值testedNumber大于16.0
assertThat(testedNumber, greaterThan(16.0));
// lessThan断言数值testedNumber小于16.0
assertThat(testedNumber, lessThan (16.0));
// greaterThanOrEqualTo断言数值 testedNumber >= 16.0
assertThat(testedNumber, greaterThanOrEqualTo (16.0));
// lessThanOrEqualTo断言testedNumber <= 16.0
assertThat(testedNumber, lessThanOrEqualTo (16.0));
// 集合相关
// hasEntry断言Map对象mapObject含有一个键值为"key"对应元素值为"value"的Entry项
assertThat(mapObject, hasEntry("key", "value" ) );
// hasItem表明迭代对象iterableObject含有元素element项则测试通过
assertThat(iterableObject, hasItem (element));
// hasKey断言Map对象mapObject含有键值“key”
assertThat(mapObject, hasKey ("key"));
// hasValue断言Map对象mapObject含有元素值value
assertThat(mapObject, hasValue(value));