2019.5.28 mockti的分析

mockito

mockito是什么

mockito是mocking框架。它能够通过其简洁的api来帮助你完成单元测试。其简单易学,其验证语法简洁还有可读性强。
Mock不是真实的对象,它只是用类型的class创建了一个虚拟对象,并可以设置对象行为。注意是设置对象的方法的预设返回内容奥!如果要通过模拟对象的方法来添加内容是不可以的,只能预设返回内容。因为这是模拟对象,不是真实对象,所以不能添加内容,而且这个模拟对象中哪些方法预设了结果,哪些方法才有输出内容,其他都返回null奥!!!!

// 设置mock对象的行为 - 当调用其get方法获取第0个元素时,返回"first"
Mockito.when(mockedList.get(0)).thenReturn("first")  
 //这个模拟的对象mockedList只有get(0)才有返回值奥,其他方法还有该方法的其他参数(get(1))都为null。

mock的基本思想:就是创建轻量级、可控制的对象来代替测试中待测试方法所需要的复杂对象(可控制的对象来代替为了编写测试为需要使用的对象)。简单直白一点呢,一个模拟对象就是一个简单的接口或者类,在里面你可以定义一个特定的方法调用之后的简单的输出

为什么要用mockito

(1)由于在测试中,往往测试某一个单元的时候(一个方法);其方法中会依赖其他类或者方法返回的内容,如果在测试方法中再构建这些依赖的数据,将会是测试很复杂和困难,实际也失去了测试的意义了
(2)所以出现了mock来模拟测试方法中所需要的数据,这样直接将所有不同内容都可以通过mock对象来模拟。就简化了测试中要创建所有不同依赖的过程了
(3)对那些不容易构建的对象用一个虚拟对象来代替,使其在调试期间用来作为真实对象的替代品。

mockito

  • 通常首先通过mockito类的方法mock()方法来模拟创建指定类型的对象
  • 然后使用这个模拟对象,作用就是使被测试方法中所依赖的方法在理论上预判出结果来支撑完成被测试方法的运行。
  • 如何使依赖的方法能输出理论预期的结果呢,就是通过mockito类先模拟这个依赖方法的对象,然后调用这个方法并输出理论预期的内容来模拟这个依赖方法输出,这样使整个测试重点只在于被测试方法,而不是被测试方法中的其他各种依赖方法了。
    (1)可以验证模拟对象的行为;即是否作出这样的行为:verify(参数是目标模拟对象).行为的方法(如add(2));
    (2)可以预期自定义模拟对象的方法的输出结果
    (3)注意!!!,模拟的对象和实际new创建的对象还是不一样的奥!比如创建list对象和模拟list对象,模拟的不可以向其中添加内容。模拟对象只能自定义输出方法的模拟预期结果,而不能进行增删改查的操作;
    模拟思想:的就是你对这个模拟对象做了什么,它才具有什么,其他没做的,这个模拟对象就什么都没有。

mockito 常用的几个方法

(1.0)验证模拟对象的行为是否发生

		@Test
   	    public void verify_behaviour(){
        //模拟创建一个List对象
        List mock = mock(List.class);  //先模拟一个对象
        //使用mock的对象
        mock.add(1);    //这只是表明这个模拟对象完成了一个添加的行为,并没有给这个模拟对象添加内容1奥
        mock.clear();
        //验证add(1)和clear()行为是否发生
        verify(mock).add(1);  //verify(mock,times(1)).add(1) //验证mock对象的add方法且参数为1是否只执行一次。
        verify(mock).clear();
    }

(1)预设什么行为抛出异常预设当什么对象,作出什么行为,抛出什么异常。

//预设当流关闭时抛出异常
        doThrow(new IOException()).when(outputStream).close();
        outputStream.close();
//举例二
	    doThrow(new RuntimeException()).when(list).add(1);
        list.add(1);

(2)预设什么行为(方法)输出什么内容:预设当对象作出(调用)什么方法,然后返回预设的结果。这样使结果在预期范围,便于对被测试方法的测试注意!!! 如果未预设的方法的返回,那么结果返回都为null。如预设get(1)返回1,那么没有预设的get(2)就没有返回值为null,
注意!!!即使你前面add()添加了,后面mock对象未预设的get(X)返回也为null。

 		public void when_thenReturn(){
        //mock一个Iterator类
        Iterator iterator = mock(Iterator.class);
        //预设当iterator调用next()时第一次返回hello,第n次都返回world
        when(iterator.next()).thenReturn("hello").thenReturn("world");            ///////////////////////////////////////
        //注意:!!!!!!!!!!!!!!!!!!!!!这边的模拟的方法中参数都必须要一致的,否则返回结果不是预期定义的那个
        //使用mock的对象
        String result = iterator.next() + " " + iterator.next() + " " + iterator.next();
        //验证结果
        assertEquals("hello world world",result);
    }


//除了匹配制定参数外,还可以匹配自己想要的任意参数
    List list = mock(List.class);
        //匹配任意参数
        when(list.get(anyInt())).thenReturn(1);
        when(list.contains(argThat(new IsValid()))).thenReturn(true);
        assertEquals(1, list.get(1));
        assertEquals(1, list.get(999));
        assertTrue(list.contains(1));
        assertTrue(!list.contains(3));
    }

(2.1)类似与上面预设调用什么方法返回什么内容。但这边返回的内容是可以自定义设置,而不是默认方式。
即 使用方法预期回调接口生成期望值(Answer结构)

	@Test
    public void answer_with_callback(){
        //使用Answer来生成我们我们期望的返回
        when(mockList.get(anyInt())).thenAnswer(new Answer<Object>() {  
        										//表示地哦啊用mockList.get(0)的方法就跳转到new Answer的answer()方法的
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                Object[] args = invocation.getArguments();
                return "hello world:"+args[0];
            }
        });
        assertEquals("hello world:0",mockList.get(0));
        assertEquals("hello world:999",mockList.get(999));
    }

注意!!!预设初始默认值的几种情况

@Test
	public void method_valuedemo() {
		//这个方式是使用系统默认的预设值的返回
		List mock1 = Mockito.mock(List.class,RETURNS_SMART_NULLS);
		System.out.println(mock1.get(2));    //返回的值就是mock1.get(2)  ,而不是直接null了。因为这边构造中有
									//智能返回非空设置,所以不是最初的null了,但也不知道是什么所以返回mock1.get(2) 
		
		//这个是使用自定义的预设值的返回。
		List mock2 = Mockito.mock(List.class,new Answer<Object>() {
			@Override
			public Object answer(InvocationOnMock invocation) throws Throwable {
				// TODO Auto-generated method stub
				return 1;
			}
		});
		//注意!!!!!!!!!这边是在构造中设置其他answer(回应)(即返回方法的返回默认初始值)
		System.out.println(mock2.get(2));   //预设所有的默认都是返回1
		System.out.println(mock2.get(4));   //预设所有的默认都是返回1    !!!!! 这边没有预设的方法的返回值就有
		System.out.println(mock2.get(7));   //预设所有的默认都是返回1



		//这是一个一个的预设值,即设置一个就返回一个这个预设值。其他没预设就没有
		List mock = Mockito.mock(List.class);
		Mockito.when(mock.get(2)).thenReturn(3);    //不使用默认预设的,而用自定义预设的值
		System.out.println(mock.get(2));    //自定义预设的返回值3
		System.out.println(mock.get(3));  //系统默认的返回值null
	}
	

	@Test
    public void unstubbed_invocations(){
        //mock对象使用Answer来对未预设的调用返回默认期望值
        List mock = mock(List.class,new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                return 999;
            }
        });
        //下面的get(1)没有预设,通常情况下会返回NULL,但是使用了Answer改变了默认期望值
        System.out.println(mock.get(1));    //返回999
        assertEquals(999, mock.get(1));
        //下面的size()没有预设,通常情况下会返回0,但是使用了Answer改变了默认期望值
        System.out.println(mock.size());    //返回999
        assertEquals(999,mock.size());
    }

自己做的一个//使用模拟对象的指定方法的时候预期回调别的方法,通过回调别的方法的结果来判断测试的成功与否注意这个回调别的对象的方法,这个对象必须是实现answer接口的实现类对象,并且实现answer接口中的answer方法

	public void whendemo() {
		List mock  = Mockito.mock(List.class);
		Mockito.when(mock.get(1)).thenAnswer(new Answer<String>() {   //实现answer接口的匿名对象

			@Override
			public String answer(InvocationOnMock invocation) throws Throwable {
				// TODO Auto-generated method stub
				Object[] args = invocation.getArguments();
				return "answer 回调"+args[0];
			}
		});
		System.out.println(mock.get(1));
		assertEquals("answer 回调1", mock.get(1));
		
	}

@Test
    public void answerTest(){
        when(mockList.get(anyInt())).thenAnswer(new CustomAnswer());
        assertEquals("hello world:0",mockList.get(0));
        assertEquals("hello world:999",mockList.get(999));
    }

    private class CustomAnswer implements Answer<String>{
        @Override
        public String answer(InvocationOnMock invocation) throws Throwable {
            Object[] args = invocation.getArguments();
            return "hello world:"+args[0];
        }
    }

用spy和mockito的区别

  • mockito是模拟对象,而spy是真实的对象。
  • Mock不是真实的对象,它只是用类型的class创建了一个虚拟对象,并可以设置对象行为
  • Spy是一个真实的对象,但它可以设置对象行为

(1)mockito(类的class):mockito mock = Mockito.mock(List.class);
(2)spy(new的对象):List list = Mockito.spy(new Arrayslist());

特别强调mockito中的when(对象.get(0)).thenReturn(3);这个方法表示对象的模拟真实方法(模拟对象的方法,如果对象是真实的就是真实对象的方法的预设返回值)。
而Mockito.doReturn(999).when(spy).get(999);表示是对对象的模拟方法的预设值,不会影响真实方法的返回内容的。
使用doReturn-when可以避免when-thenReturn调用本引用(真实)对象api。

	@SuppressWarnings("unchecked")
	@Test  //(expected = IndexOutOfBoundsException.class)
    public void spy_on_real_objects(){
        List list = new LinkedList<>();
        List spy = Mockito.spy(list);
        spy.add(1);
        spy.add(2);
        System.out.println(spy.get(0));
        
        //下面预设的spy.get(4)会报错,因为会调用真实对象的get(4),所以会抛出越界异常
//      Mockito.when(spy.get(4)).thenReturn(6);
//      System.out.println(spy.get(4));
        	
        //使用doReturn-when可以避免when-thenReturn调用真实对象api
        Mockito.doReturn(6).when(spy).get(4);
        System.out.println(spy.get(4));
        
//        Mockito.when(spy.size()).thenReturn(100);  //这个也是可以的
        Mockito.doReturn(1000).when(spy).size();   //但为了统一上面的描述,最好还是用doreturn的预设方式,都用虚拟的
        System.out.println(spy.size());
        
    }

@Test  //预设方法的返回值
	public void methoddemo() {
		List mock = Mockito.mock(List.class);  // 模拟对象
		List spy = Mockito.spy(new ArrayList());  //真实对象
		Mockito.when(mock.get(1)).thenReturn(2);   //都是对象的正式方法。
		//Mockito.when(spy.get(1)).thenReturn(4);   //这句话就会越界异常,因为真实对象还没有第二元素。
		Mockito.doReturn(22).when(spy).get(33);    //都是对象的模拟方法。
		Mockito.doReturn(22).when(mock).get(33);
		System.out.println(mock.get(33));   //22
		System.out.println(spy.get(33));   //22
		assertEquals(2, mock.get(1));
		System.out.println(mock.get(1));  //2
		System.out.println(mock.get(2));  //null
		
	}

连续调用预期返回值

@Test(expected = RuntimeException.class)
    public void consecutive_calls(){
        //模拟连续调用返回期望值,如果分开,则只有最后一个有效
		List mockList = Mockito.mock(List.class);
		Mockito.when(mockList.get(0)).thenReturn(0);
		System.out.println(mockList.get(0));
		Mockito.when(mockList.get(0)).thenReturn(1);
		System.out.println(mockList.get(0));
		Mockito.when(mockList.get(0)).thenReturn(2);
		System.out.println(mockList.get(0));
		Mockito.when(mockList.get(1)).thenReturn(0).thenReturn(1).thenThrow(new RuntimeException());
		System.out.println(mockList.get(0));
        assertEquals(2,mockList.get(0));
        assertEquals(2,mockList.get(0));
        assertEquals(0,mockList.get(1));
        assertEquals(1,mockList.get(1));
        //第三次或更多调用都会抛出异常
        mockList.get(1);
    }

小结:

Mockito并不是创建一个真实的对象,而是模拟这个对象,他用简单的when(mock.method(params)).thenRetrun(result)语句设置mock对象的行为,如下语句:

// 设置mock对象的行为 - 当调用其get方法获取第0个元素时,返回"first"
Mockito.when(mockedList.get(0)).thenReturn("first");

实际Mock创建对象的本质,可以理解为创建一个proxy对象,保存被调用的方法名(get),以及调用时候传递的参数(0),然后在调用thenReturn方法时再把“first”保存起来,这样,就有了构建一个stub方法所需的所有信息,构建一个stub。当get方法被调用的时候,实际上调用的是之前保存的proxy对象的get方法,返回之前保存的数据。

Spring Boot应用的测试——Mockito

今天总结下如何用Mock对象测试Controller层的代码。

第三个测试用例中展示了如何通过MockMvc对象实现跟第二个测试类似的功能。Spring测试框架提供MockMvc对象,可以在不需要客户端-服务端请求的情况下进行MVC测试,完全在服务端这边就可以执行Controller的请求,跟启动了测试服务器一样。

测试开始之前需要建立测试环境,setup方法被@Before修饰。通过MockMvcBuilders工具,使用WebApplicationContext对象作为参数,创建一个MockMvc对象。

MockMvc对象提供一组工具函数用来执行assert判断,都是针对web请求的判断。这组工具的使用方式是函数的链式调用,允许程序员将多个测试用例链接在一起,并进行多个判断。在这个例子中我们用到下面的一些工具函数:

perform(get(…))建立web请求。在我们的第三个用例中,通过MockMvcRequestBuilder执行GET请求。
andExpect(…)可以在perform(…)函数调用后多次调用,表示对多个条件的判断,这个函数的参数类型是ResultMatcher接口,在MockMvcResultMatchers这这个类中提供了很多返回ResultMatcher接口的工具函数。这个函数使得可以检测同一个web请求的多个方面,包括HTTP响应状态码(response status),响应的内容类型(content type),会话中存放的值,检验重定向、model或者header的内容等等。这里需要通过第三方库json-path检测JSON格式的响应数据:检查json数据包含正确的元素类型和对应的值,例如jsonPath("$.name").value(“中文测试”)用于检查在根目录下有一个名为name的节点,并且该节点对应的值是“中文测试”。

实验

package com.example;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

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.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
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(classes = SpringBoot05JpaApplication.class)
public class SpringBoot05JpaApplicationTests {

	 @Autowired
	 //(1)这个就是对应上面要测试的类的所在容器
	 //这是web容器,这只要是关联上面的SpringBoot05JpaApplication.class项目中的容器。然后通过这个容器在第二步(@before)中产生关联的
//	对应的模拟客户端。最后就可以由这个模拟客户端进行一些请求操作了
	    private WebApplicationContext webApplicationContext;
	 
	 //	(2)这个对象就相当于模拟的客户端对象
//	 Spring测试框架提供MockMvc对象,可以在不需要客户端-服务端请求的情况下进行MVC测试,
//	 完全在服务端这边就可以执行Controller的请求,跟启动了测试服务器一样
	    private MockMvc mockMvc;

	
	@Before
    public void setUp() throws Exception{
        //MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;
//(3)这边通过本地的web容器来注册生成关联本地的模拟的客户端对象
		mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();//建议使用这种
    }
	
/*	@Test
	public void contextLoads() throws SQLException {

		System.out.println(dao.getClass());
	System.out.println(dataSource.getClass());
	Connection connection = dataSource.getConnection();
	System.out.println(connection);
	}*/
	  /**
	   * 模拟客户端所能做的行为
     * 1、mockMvc.perform执行一个请求。
     * 2、MockMvcRequestBuilders.get("XXX")构造一个请求。
     * 3、ResultActions.param添加请求传值
     * 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
     * 5、ResultActions.andExpect添加执行完成后的断言。
     * 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
     *   比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
     * 5、ResultActions.andReturn表示执行完成后返回相应的结果。
     */
	
	@Test
	public void controllertest() throws Exception {
		  MvcResult mvcResult = mockMvc.perform(get("/adduser").param("username", "abc"))
				  .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
	        //获取状态码
	        int status = mvcResult.getResponse().getStatus();
	        System.out.println(mvcResult.getResponse().getContentAsString());
	        System.out.println(status);
	}
	
}
=====================================
测试的结果:
Hibernate: insert into tb_user (age, gender, username) values (?, ?, ?)
{"id":14,"username":"abc","gender":null,"age":null}
200

上面已经理解了模拟客户端的整个原理,下面只要知道这些客户端对象能够具有哪些请求操作就彻底了解模拟的整个web过程中发起请求、给予响应的整个过程:

RequestBuilder/MockMvcRequestBuilders:

在上面的测试类中,我们用到了这么一个类MockMvcRequestBuilders用来构建请求的,此类有以下主要的API:

MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder;如get(/user/{id}, 1L);
MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables):同get类似,但是是POST方法;
MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables):同get类似,但是是PUT方法;
MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) :同get类似,但是是DELETE方法;
MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables):同get类似,但是是OPTIONS方法;
MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables): 提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API;
MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables):提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder;
RequestBuilder asyncDispatch(final MvcResult mvcResult):创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder;

MockMvcRequestBuilders通过方法得到两类Builder,一个是MockHttpServletRequestBuilder ,一个是MockMultipartHttpServletRequestBuilder (上传文件)

MockHttpServletRequestBuilder:

MockHttpServletRequestBuilder header(String name, Object... values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders):添加头信息;
MockHttpServletRequestBuilder contentType(MediaType mediaType):指定请求的contentType头信息;
MockHttpServletRequestBuilder accept(MediaType... mediaTypes)/MockHttpServletRequestBuilder accept(String... mediaTypes):指定请求的Accept头信息;
MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content):指定请求Body体内容;
MockHttpServletRequestBuilder param(String name,String... values):请求传入参数
MockHttpServletRequestBuilder cookie(Cookie... cookies):指定请求的Cookie;
MockHttpServletRequestBuilder locale(Locale locale):指定请求的Locale;
MockHttpServletRequestBuilder characterEncoding(String encoding):指定请求字符编码;
MockHttpServletRequestBuilder requestAttr(String name, Object value) :设置请求属性数据;
MockHttpServletRequestBuilder sessionAttr(String name, Object value)/MockHttpServletRequestBuilder sessionAttrs(Map<string, object=""> sessionAttributes):设置请求session属性数据;
MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map<string, object=""> flashAttributes):指定请求的flash信息,比如重定向后的属性信息;
MockHttpServletRequestBuilder session(MockHttpSession session) :指定请求的Session;
MockHttpServletRequestBuilder principal(Principal principal) :指定请求的Principal;
MockHttpServletRequestBuilder contextPath(String contextPath) :指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾;
MockHttpServletRequestBuilder pathInfo(String pathInfo) :请求的路径信息,必须以“/”开头;
MockHttpServletRequestBuilder secure(boolean secure):请求是否使用安全通道;
MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor):请求的后处理器,用于自定义一些请求处理的扩展点;

================================================================
/**
 * 获取教程测试用例
 * @throws Exception
 */
@Test
public void qryLearn() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/learn/resource/1001")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .accept(MediaType.APPLICATION_JSON_UTF8)
                .session(session)
        )
       .andExpect(MockMvcResultMatchers.status().isOk())
       .andExpect(MockMvcResultMatchers.jsonPath("$.author").value("嘟嘟MD独立博客"))
       .andExpect(MockMvcResultMatchers.jsonPath("$.title").value("Spring Boot干货系列"))
       .andDo(MockMvcResultHandlers.print());
}
-----------------------------------------------------------------

    mockMvc.perform执行一个请求
    MockMvcRequestBuilders.get(“/user/1”)构造一个请求,Post请求就用.post方法
    contentType(MediaType.APPLICATION_JSON_UTF8)代表发送端发送的数据格式是application/json;charset=UTF-8
    accept(MediaType.APPLICATION_JSON_UTF8)代表客户端希望接受的数据类型为application/json;charset=UTF-8
    session(session)注入一个session,这样拦截器才可以通过
    ResultActions.andExpect添加执行完成后的断言
    ResultActions.andExpect(MockMvcResultMatchers.status().isOk())方法看请求的状态响应码是否为200如果不是则抛异常,测试不通过
    andExpect(MockMvcResultMatchers.jsonPath(“$.author”).value(“嘟嘟MD独立博客”))这里jsonPath用来获取author字段比对是否为嘟嘟MD独立博客,不是就测试不通过
    ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息

MockMultipartHttpServletRequestBuilder:

MockMultipartHttpServletRequestBuilder继承自MockHttpServletRequestBuilder,又提供了如下API:

MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder
 file(MockMultipartFile file):指定要上传的文件;

ResultActions:

调用MockMvc.perform(RequestBuilder requestBuilder)后将得到ResultActions;(perform方法,入参是请求,出参是结果)通过ResultActions完成如下三件事:

ResultActions andExpect(ResultMatcher matcher) :添加验证断言来判断执行请求后的结果是否是预期的;
ResultActions andDo(ResultHandler handler) :添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;
MvcResult andReturn() :返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;

ResultMatcher/MockMvcResultMatchers:

ResultMatcher用来匹配执行完请求后的结果验证,其就一个match(MvcResult result)断言方法,如果匹配失败将抛出相应的异常;此类案例中并为使用,请自行查看。具体提供以下API:

HandlerResultMatchers handler():请求的Handler验证器,比如验证处理器类型/方法名;此处的Handler其实就是处理请求的控制器;
RequestResultMatchers request():得到RequestResultMatchers验证器;
ModelResultMatchers model():得到模型验证器;
ViewResultMatchers view():得到视图验证器;
FlashAttributeResultMatchers flash():得到Flash属性验证;
StatusResultMatchers status():得到响应状态验证器;
HeaderResultMatchers header():得到响应Header验证器;
CookieResultMatchers cookie():得到响应Cookie验证器;
ContentResultMatchers content():得到响应内容验证器;
JsonPathResultMatchers jsonPath(String expression, Object ... args)/ResultMatcher jsonPath(String expression, Matcher matcher):得到Json表达式验证器;
XpathResultMatchers xpath(String expression, Object... args)/XpathResultMatchers xpath(String expression, Map<string, string=""> namespaces, Object... args):得到Xpath表达式验证器;
ResultMatcher forwardedUrl(final String expectedUrl):验证处理完请求后转发的url(绝对匹配);
ResultMatcher forwardedUrlPattern(final String urlPattern):验证处理完请求后转发的url(Ant风格模式匹配,@since spring4);
ResultMatcher redirectedUrl(final String expectedUrl):验证处理完请求后重定向的url(绝对匹配);
ResultMatcher redirectedUrlPattern(final String expectedUrl):验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4);

SpringBoot 使用MockMvc进行Controller的测试

package com.example;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.example.controller.person;

/*这个测试controller层的核心:(1)mockMvc  模拟客户端对象(2)mockmvcBuliders 客户端对象的构建器工具,(3)整个应用的容器。    
 * 
 * 如果只是针对Service层做测试,就像普通测试一样,但是有时候需要对Controller层(API)做测试,这时候就得用到MockMvc了,
 * 你可以不必启动工程就能测试这些接口。MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用
 * 这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。
 * 
 * mvc客户端对象:     真实客户端中,实际作用就是将请求url封装为http请求对象,然后里面可以设置一些请求的一些参数信息(请求行中设置一些键值对信息)
 * 
 * (1)所以首先请求对象工具MockMvcRequestBuilder;发起请求get ,并将请求内容封装   
 * 			返回一个httpServlet请求的对象(这个对象就类似于一个http请求体对象,所以这个对象可以设置一些其他请求参数;如session,
 * 请求体内容,请求头信息,指定请求的Cookie,请求字符编码)
 * (2)mvc客户端     客户端对象的执行请求的方法 perform    ,这个方法用于执行上面的       封装请求的对象     ,并返回的是模拟请求的      返回结果对象(类似于响应)
 * 
 * (3)mvc模拟客户端对象——————完成的作用就是模拟浏览器访问服务端的整个过程:即执行路径请求——然后有输入参数就带上——最后返回一个请求的响应对象。
 * 流程:
 * 1.首先MockMvcRequestBuilders封装请求路径(/xxx),返回一个MockHttpServletRequestBuilder对象,这个对象是类似于真正浏览器封装
 * 的http请求对象,这个对象中可以设置请求对象一些参数(如session,请求体内容,请求头信息,指定请求的Cookie,请求字符编码)。
 * 2.上面请求对象封装好了,然后mvc客户端就通过perform方法执行这个http请求对象了,类比实际客户端,这样执行后就返回响应对象 了,后端响应过程都
 * 封装了。
 * 3.下面就针对这个响应对象开始结果的验证:可以验证返回的状态码(.andExpect(MockMvcResultMatchers.status().isOk())表示成功状态200)
 * 获取一个响应体对象,然后自定义验证服务端响应的内容。可以验证响应体中具体的响应内容
 * 
*/

@RunWith(SpringRunner.class)   //Junit的运行器,一般是会有默认的运行器(在 JUnit 中有很多个 Runner ,    明确用哪个运行器运行测试代码
//他们负责调用你的测试代码;使用了系统默认的TestClassRunner)      对于Spring单元测试总是要使用 SpringRunner.class . 
@SpringBootTest  ////SpringBootTest 是springboot 用于测试的注解,可指定启动类或者测试环境等,这里直接默认。
//@WebMvcTest(person.class)   //  可以对于明确指定的一个controller类测试然后直接注入MVC,不用单独创建mvc对象

@AutoConfigureMockMvc
public class SpringBoot10ApplicationTests {
	/*@Autowired
	//这个context是真实的这个应用中的容器对象。作用是:为了产生一个和这个应用关联的模拟客户端对象的
//	applicationContext变量,就是spring的全局IOC容器,通过它可以获取在xml中定义的bean
	private WebApplicationContext  context;*/
	
	//这边mvc是一个模拟的客户端对象奥
	@Autowired
	private MockMvc mvc;
	
/*	@Before
	public void Setup() {		
//		MockMvcBuilders这是测试类中模拟客户端对象的一个工具类。
//		创建好   模拟客户端对象    后,这个客户端对象就有好多方法来模拟客户端和服务端的许多操作了
	mvc = MockMvcBuilders.webAppContextSetup(context).build();
	}*/
		
	@Test   //@Test注解: JUnit在执行每个测试方法之前, 都会为测试类创建一个新的实例, 这样有助于隔离各个测试方法之前的相互影响. 
	public void contextLoads() throws Exception {
		//这个mockHttpServletRequestBuilder就相当于封装请求内容的对象。并返回一个httpServlet形式的请求对象
//		MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/getallschool");
//		MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/getallstudent");
		
		//这一步是将请求内容封装为一个对象,即类似于真实浏览器将请求url封装为一个完整协议的http请求对象一样。web必须按照http协议奥!
		MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/addstudnet");
		//这是http请求对象中封装一些请求的参数信息,当然请求对象中也可以封装其他参数如session、字符编码集、请求头信息、请求体信息。
		mockHttpServletRequestBuilder.param("name", "77").param("age", "77").param("gender", "0");		
		//这一步是客户端模拟器真正执行请求,然后返回浏览器请求的响应对象。下面主要围绕这个响应内容来测试的奥
		ResultActions perform = mvc.perform(mockHttpServletRequestBuilder);   //然后这个客户端执行请求
		//验证响应对象中状态码是否是200,即访问成功。
		ResultActions andExpect = perform.andExpect(MockMvcResultMatchers.status().isOk());
		//这一步是响应对象中直接获取响应体部分的内容信息。获取 了响应体中响应内容,就可以自定义验证一些我们后端自己开发的内容是否一致啦
		MvcResult andReturn = andExpect.andReturn();
		
		//这个response对象是http返回响应对象的响应体对象(当然里面有好多响应的信息啦)
		MockHttpServletResponse response = andReturn.getResponse();				
//		返回的结果————————输出结果类型          application/json;charset=UTF-8
		String contentType = response.getContentType();
		System.out.println("输出结果类型          "+contentType);		
//		这是获取请求的真正的响应内容
		String contentAsString = response.getContentAsString();	
		String[] split = contentAsString.split(",");
		for (String string : split) {
			System.out.println(string);
		}
		System.out.println(contentAsString);
	}
}
/*
 * 输出结果类型          application/json;charset=UTF-8
{"sid":0
"name":"77"
"age":77
"gender":0}
{"sid":0,"name":"77","age":77,"gender":0}
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值