定义:
1.JUnit4.*引入了Hamcrest框架,Hamcest提供了一套匹配符Matcher,这些匹配符更接近自然语言,可读性高,更加灵活;
2.使用全新的断言语法:assertThat,结合Hamcest提供的匹配符,只用这一个方法,就可以实现所有的测试;
3.assertThat语法如下:
assertThat(T actual, Matcher matcher);
assertThat(String reason, T actual, Matcher matcher);
其中actual为需要测试的变量,matcher为使用Hamcrest的匹配符来表达变量actual期望值的声明;
4.注意事项:
a.必须导入JUnit4.4之后的版本才能使用assertThat方法;
b.不需要继承TestCase类,但是需要测试方法前必须加“@Test”。
注解:
@Before | 初始化方法 |
---|---|
@After | 释放资源 |
@Test | 测试方法,在这里可以测试期望异常和超时时间 |
@Ignore | 忽略的测试方法 |
@BeforeClass | 针对所有测试,只执行一次,且必须为static void |
@AfterClass | 针对所有测试,只执行一次,且必须为static void |
@RunWith | 指定测试类使用某个运行器 |
@Parameters | 指定测试类的测试数据集合 |
@Rule | 允许灵活添加或重新定义测试类中的每个测试方法的行为 |
@FixMethodOrder | 指定测试方法的执行顺序 |
一个测试类单元测试的执行顺序为:
@BeforeClass –> @Before –> @Test –> @After –> @AfterClass
代码:
sercice代码:
public class DemoService{
public int add(int a, int b) {
return a+b;
}
public double div(double a, double b) {
return a / b;
}
public String getName(String name) {
return name;
}
public List<String> getList(String item) {
List<String> l = new ArrayList<String>();
l.add(item);
return l;
}
public Map<String, String> getMap(String key, String value) {
Map<String, String> m = new HashMap<String, String>();
m.put(key, value);
return m;
}
}
测试代码
public class Testas {
@Test
public void testAdd() {
int s = new DemoService().add(1,2);
//allOf:所有条件必须都成立,测试才通过
assertThat(s,allOf(greaterThan(1),lessThan(5)));
//anyOf:只要有一个条件成立,测试就通过
assertThat(s,anyOf(greaterThan(1),lessThan(5)));
//anything:无论什么条件,测试都通过
assertThat(s, anything());
//is:变量的值等于指定值时,测试通过
assertThat(s, is(3));
//is匹配符简写,is(equalTo(x))的简写,断言testedValue等于expectedValue
int testedValue = 6;
int expectedValue = 6;
assertThat(testedValue, is(expectedValue));
//not:和is相反,变量的值不等于指定值时,测试通过
assertThat(s, not(1));
}
@Test
public void testDiv(){
//数值匹配符
double d = new DemoService().div(10, 3);
//closeTo:浮点型变量的值在3.0±0.5范围内,测试通过
assertThat(d, closeTo(3.0, 0.5));
//greaterThan:变量的值大于指定值时,测试通过
assertThat(d, greaterThan(3.0));
//lessThan:变量的值小于指定值时,测试通过
assertThat(d, lessThan(3.5));
//greaterThanOrEuqalTo:变量的值大于等于指定值时,测试通过
assertThat(d, greaterThanOrEqualTo(3.3));
//lessThanOrEqualTo:变量的值小于等于指定值时,测试通过
assertThat(d, lessThanOrEqualTo(3.4));
}
@Test
public void testString(){
//字符串匹配符
String n = new DemoService().getName("Magci");
//containsString:字符串变量中包含指定字符串时,测试通过
assertThat(n, containsString("ci"));
//startsWith:字符串变量以指定字符串开头时,测试通过
assertThat(n, startsWith("Ma"));
//endsWith:字符串变量以指定字符串结尾时,测试通过
assertThat(n, endsWith("i"));
//euqalTo:字符串变量等于指定字符串时,测试通过
assertThat(n, equalTo("Magci"));
//equalToIgnoringCase:字符串变量在忽略大小写的情况下等于指定字符串时,测试通过
assertThat(n, equalToIgnoringCase("magci"));
//equalToIgnoringWhiteSpace:字符串变量在忽略头尾任意空格的情况下等于指定字符串时,测试通过
assertThat(n, equalToIgnoringWhiteSpace(" Magci "));
}
@Test
public void testList(){
//集合匹配符
List<String> l = new DemoService().getList("Magci");
//hasItem:Iterable变量中含有指定元素时,测试通过
assertThat(l, hasItem("Magci"));
}
@Test
public void testMap(){
Map<String, String> m = new DemoService().getMap("tr", "String");
//hasEntry:Map变量中含有指定键值对时,测试通过
assertThat(m, hasEntry("tr", "String"));
//hasKey:Map变量中含有指定键时,测试通过
assertThat(m, hasKey("tr"));
//hasValue:Map变量中含有指定值时,测试通过
assertThat(m, hasValue("String"));
}
}
@Test
public void testClass(){
DemoServicedemoas = new DemoService();
//is(instanceOf(XXX.class)):断言demoas为Demoas的实例
assertThat(demoas,is(instanceOf(DemoService.class)));
//isA(xxx.class):是is(instanceOf(XXX.class))的简写
assertThat(demoas,isA(DemoService.class));
}
方法一:注解上添加异常类Error.class或者Exception.class
@RunWith(SpringRunner.class)
@SpringBootTest
public class Testas {
@Test(expected = Error.class)
public void testAdd() {
}
}
方法二:直接抛出,抛出以下的代码不执行
@Test
public void testAdd() {
int s = new DemoService().add(1,2);
//allOf:所有条件必须都成立,测试才通过
assertThat(s,allOf(greaterThan(1),lessThan(5)));
throw new Error();
}
注:两个方法可以同时存在,但是要保证上下抛出的异常一致性
@Test(expected = Error.class)
public void testAdd() {
int s = new DemoService().add(1,2);
//allOf:所有条件必须都成立,测试才通过
assertThat(s,allOf(greaterThan(1),lessThan(5)));
throw new Error();
}
错误示范:会导致异常不通过
@Test(expected = Exception.class)
public void testAdd() {
int s = new DemoService().add(1,2);
//allOf:所有条件必须都成立,测试才通过
assertThat(s,allOf(greaterThan(1),lessThan(5)));
throw new Error();
}
代码包括:
上面的service代码
Controller代码:
@RestController
public class DemoController {
@Autowired
private DemoService demoService;
@GetMapping("/test/{a}/{b}")
public int addCon(@PathVariable int a, @PathVariable int b){
return demoService.add(a,b);
}
}
测试代码:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class TestMock {
@Autowired
private MockMvc mvc;
@Test
public void testMocks() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/test/5/6")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("11"))
.andDo(MockMvcResultHandlers.print());
}
注:
这一步集成Web环境测试需要在Test方法之前使用,但是可以替代,如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:config/IncotermsRestServiceTest-context.xml")
@WebAppConfiguration
public class IncotermsRestServiceTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); //构造MockMvc
}
...
}
替代:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class TestMock {
@Autowired
private MockMvc mvc;
}
MockMvc如何抛出异常
public TestEntity adds(){
TestEntity testEntity1 = new TestEntity();
testEntity1.setAuthor("测试");
testEntity1.setTitle("第一次");
return testEntity1;
}
controller层
@GetMapping("/test")
public TestEntity addCon(){
return demoService.adds();
}
测试层
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class TestMock {
@Autowired
private MockMvc mvc;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void throwsNothing(){
//没有预期的例外,没有抛出:传递。
}
@Test
public void testMocks() throws Exception {
ResultActions persform =mvc.perform(MockMvcRequestBuilders.get("/test/5/6")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8));
persform.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("11"));
thrown.expect(AssertionError.class);
thrown.expectMessage("JSON path\"$.author\"");
persform.andExpect(MockMvcResultMatchers.jsonPath("$.author").value("测试1"));
persform.andExpect(MockMvcResultMatchers.jsonPath("$.title").value("第一次"));
}
}
此时运行结果是:
你必须将ExpectedException规则添加到测试中。这不会影响你现有的测试。在指定预期异常的类型后,如果抛出此类异常,则测试成功,如果抛出异常或没有异常则失败。
assertThat断言主要用来测试Service层
MockMvc主要用来测试Controller层
如果是需要多个单元测试类整合测试 使用一个Runner进行异步测试,只需要把相关的class放入到SuiteClasses{}中即可,如:JunitTest.class 和 TestClassDemo.class 都是写好的单元测试类.
@RunWith(Suite.class)
@SpringBootTest
@Suite.SuiteClasses({
Testas.class,
TestMock.class
})
public class DemoApplicationTests {
//不需要写任何代码
}
测试普通控制器
mockMvc.perform(get("/user/{id}", 1)) //执行请求
.andExpect(model().attributeExists("user")) //验证存储模型数据
.andExpect(view().name("user/view")) //验证viewName
.andExpect(forwardedUrl("/WEB-INF/jsp/user/view.jsp"))//验证视图渲染时forward到的jsp
.andExpect(status().isOk())//验证状态码
.andDo(print()); //输出MvcResult到控制台
得到MvcResult自定义验证
MvcResult result = mockMvc.perform(get("/user/{id}", 1))//执行请求
.andReturn(); //返回MvcResult
Assert.assertNotNull(result.getModelAndView().getModel().get("user")); //自定义断言
验证请求参数绑定到模型数据及Flash属性
mockMvc.perform(post("/user").param("name", "zhang")) //执行传递参数的POST请求(也可以post("/user?name=zhang"))
.andExpect(handler().handlerType(UserController.class)) //验证执行的控制器类型
.andExpect(handler().methodName("create")) //验证执行的控制器方法名
.andExpect(model().hasNoErrors()) //验证页面没有错误
.andExpect(flash().attributeExists("success")) //验证存在flash属性
.andExpect(view().name("redirect:/user")); //验证视图
文件上传
byte[] bytes = new byte[] {1, 2};
mockMvc.perform(fileUpload("/user/{id}/icon", 1L).file("icon", bytes)) //执行文件上传
.andExpect(model().attribute("icon", bytes)) //验证属性相等性
.andExpect(view().name("success")); //验证视图
JSON请求/响应验证
String requestBody = "{\"id\":1, \"name\":\"zhang\"}";
mockMvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON).content(requestBody)
.accept(MediaType.APPLICATION_JSON)) //执行请求
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) //验证响应contentType
.andExpect(jsonPath("$.id").value(1)); //使用Json path验证JSON 请参考http://goessner.net/articles/JsonPath/
String errorBody = "{id:1, name:zhang}";
MvcResult result = mockMvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON).content(errorBody)
.accept(MediaType.APPLICATION_JSON)) //执行请求
.andExpect(status().isBadRequest()) //400错误请求
.andReturn();
Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));//错误的请求内容体
异步测试
//Callable
MvcResult result = mockMvc.perform(get("/user/async1?id=1&name=zhang")) //执行请求
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class))) //默认会等10秒超时
.andReturn();
mockMvc.perform(asyncDispatch(result))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id").value(1));
全局配置
mockMvc = webAppContextSetup(wac)
.defaultRequest(get("/user/1").requestAttr("default", true)) //默认请求 如果其是Mergeable类型的,会自动合并的哦mockMvc.perform中的RequestBuilder
.alwaysDo(print()) //默认每次执行请求后都做的动作
.alwaysExpect(request().attribute("default", true)) //默认每次执行后进行验证的断言
.build();
mockMvc.perform(get("/user/1"))
.andExpect(model().attributeExists("user"));
参考:https://www.jianshu.com/p/91045b0415f0
参考:https://blog.csdn.net/wangpeng047/article/details/9628449
参考: https://my.oschina.net/u/2349331/blog/471247