JUnit中使用assertThat断言与MockMvc

定义:
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();
        }

MockMvc的用法

代码包括:
上面的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());
    }

测试结果:
JUnit中使用assertThat断言与MockMvc_第1张图片
整个过程:

  1. mockMvc.perform执行一个请求;
  2. MockMvcRequestBuilders.get("/user/1")构造一个请求
  3. ResultActions.andExpect添加执行完成后的断言
  4. ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用
  5. MockMvcResultHandlers.print()输出整个响应结果信息。
  6. ResultActions.andReturn表示执行完成后返回相应的结果。

注:

这一步集成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如何抛出异常

  1. 添加注解属性
    在这里插入图片描述
    可以指定抛出的类型,如果一致则通过。但是会在某个位置抛出,不一定特定的某行

  2. 提示异常信息
    业务层


    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 {
//不需要写任何代码
}

结果:
JUnit中使用assertThat断言与MockMvc_第2张图片

一些常用的测试

测试普通控制器

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

你可能感兴趣的:(JUnit中使用assertThat断言与MockMvc)