51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1169|回复: 0
打印 上一主题 下一主题

[转贴] 单元测试模拟框架Mockito

[复制链接]
  • TA的每日心情
    擦汗
    5 小时前
  • 签到天数: 1046 天

    连续签到: 4 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-3-22 11:12:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    概览
      Mockito 是Java中用于单元测试的模拟框架。
      引入 pom 依赖
    <dependency>
          <groupId>org.mockito</groupId>
          <artifactId>mockito-all</artifactId>
          <version>LATEST</version>
      </dependency>

    启用 Mockito
      通过class参数(类、接口)创建一个mock对象,该对象与真实创建的对象有所区别。使用 Mockito 类的一系列静态方法。
    1.   public static <T> T mock(Class <T> classToMock)
    复制代码
    编写一个 Mockito 示例
      Mockito 需要依赖[url=]Junit[/url]
    1. package org.byron4j.cookbook.mocketio.basic;
    2.   import org.junit.Test;
    3.   import org.mockito.Mockito;
    4.   import java.util.ArrayList;
    5.   import java.util.List;
    6.   import static org.junit.Assert.assertEquals;
    7.   public class MockitoAnnotationTest {
    8.       @Test
    9.       public void whenNotUseMockAnnotation_thenCorrect() {
    10.           // 创建一个mock出来的ArrayList对象
    11.           List mockList = Mockito.mock(ArrayList.class);
    12.           // 调用mock对象的方法
    13.           mockList.add("one");
    14.           //mockList.add("one");
    15.           // 获取mock对象的实际方法,获取size,结果为0
    16.           System.out.println("mockList.size(): " + mockList.size());
    17.           // toString方法
    18.           System.out.println("mockList's toString is: " + mockList);
    19.           // 验证mock对象mockList的add方法是否被调用了一次
    20.           Mockito.verify(mockList).add("one");
    21.           assertEquals(0, mockList.size());
    22.           // 当调用mockList.size()的时候,总是返回100
    23.           Mockito.when(mockList.size()).thenReturn(100);
    24.           assertEquals(100, mockList.size());
    25.       }
    26.   }
    复制代码
    运行输出结果为:
    1. mockList.size(): 0
    2.   mockList's toString is: Mock for ArrayList, hashCode: 409962262
    复制代码
     ·使用List mockList = Mockito.mock(ArrayList.class);创建一个mock出来的ArrayList对象mockList;
      · 调用mock对象的方法mockList.add("one");
      · 然后调用mockList.size()结果为0,表明mockList.add("one");代码仅仅表示发生了add行为本身,并不会对mockList的其他行为产生影响;
      · verify方法,验证mock对象mockList的add方法是否被调用了一次。
    1. Mockito.verify(mockList).add("one");
    2.   assertEquals(0, mockList.size());
    复制代码
    · 当调用mockList.size()的时候,总是返回100
    1.  Mockito.when(mockList.size()).thenReturn(100);
    2.   assertEquals(100, mockList.size());
    复制代码
    verify 方法
      验证包含的行为(方法)发生过一次(被调用一次),即verify(mock, times(1)),例如: verify(mock).someMethod("some arg");。等效于 verify(mock, times(1)).someMethod("some arg");
    1. Mockito.verify(mockList).add("one");
    复制代码
    等效于:
    1.  Mockito.verify(mockList, Mockito.times(1)).add("one");
    复制代码
    Mockito.times(1) 参数1表示期望执行的次数是1。
      verify 方法传入两个参数:mock对象、验证模式
     public static <T> T verify(T mock,  VerificationMode mode);

    Mockito.times(int wantedNumberOfInvocations) 可以得到一个VerificationMode对象,实际调用了 VerificationModeFactory.times(wantedNumberOfInvocations)获取到一个Times对象:new Times(wantedNumberOfInvocations),Times实现了VerificationMode接口。
      参数一: mock 对象,必须的。
      参数二: 验证模式:times(x), atLeastOnce() 或者 never() 等;如果是times(1)则可忽略该参数。
      times方法调用栈如下:
    1. org.mockito.Mockito#times(int wantedNumberOfInvocations)
    2.   org.mockito.internal.verification.VerificationModeFactory#times(int wantedNumberOfInvocations)
    3.   org.mockito.internal.verification.Times(int wantedNumberOfInvocations)
    复制代码
    when 方法
      Mockito.when方法定义如下:
    1. public static <T> OngoingStubbing<T> when(T methodCall)
    复制代码
    when方法需要传递一个  mock对象的方法 的调用,例如本例中我们传递了mock对象mockList的mockList.size()方法的调用。
      when方法会留一份存根,在我们希望模拟在特定情况下返回特定的返回值时,回调用它。
      简单的意图就是: 当x方法调用的时候,就返回y。
      示例:
      ·when(mock.someMethod()).thenReturn(10); : 调用方法时返回10。
      · when(mock.someMethod(anyString())).thenReturn(10); : 灵活参数。
      · when(mock.someMethod("some arg")).thenThrow(new RuntimeException()); : 调用方法时,抛出一个异常。
      · when(mock.someMethod("some arg")).thenThrow(new RuntimeException()).thenReturn("foo"); : 连续调用不同的行为。
      · when(mock.someMethod("some arg")).thenReturn("one", "two"); : 连续的存根,第一次调用返回"one",第二次以及之后的调用返回"two"。
      · when(mock.someMethod("some arg")).thenReturn("one").thenReturn("two"); : 和上面一条等同效果。
      · when(mock.someMethod("some arg")).thenThrow(new RuntimeException(), new NullPointerException(); : 连续存根,抛异常。
    1.  @Test
    2.   public void whenTest() {
    3.       List mock = Mockito.mock(List.class);
    4.       Mockito.when(mock.size()).thenReturn(-1);
    5.       System.out.println("mock.size():" + mock.size());
    6.       // 连续存根
    7.       Mockito.when(mock.size()).thenReturn(1).thenReturn(2).thenReturn(3);
    8.       for(int i=1; i <= 5; i++){
    9.           System.out.println("=====连续存根方式1:=====: " + mock.size());
    10.       }
    11.       Mockito.when(mock.size()).thenReturn(1,2, 3);
    12.       for(int i=1; i <= 5; i++){
    13.           System.out.println("#####连续存根方式2:#####: " + mock.size());
    14.       }
    15.       // 模拟异常
    16.       Mockito.when(mock.size()).thenThrow(new RuntimeException(), new NullPointerException());
    17.       try{
    18.           mock.size();
    19.       }catch (Exception e){
    20.           System.out.println(e);
    21.       }
    22.       try{
    23.           mock.size();
    24.       }catch (Exception e){
    25.           System.out.println(e);
    26.       }
    27.   }
    复制代码
    运行输出:
    1.  mock.size():-1
    2.   =====连续存根方式1:=====: 1
    3.   =====连续存根方式1:=====: 2
    4.   =====连续存根方式1:=====: 3
    5.   =====连续存根方式1:=====: 3
    6.   =====连续存根方式1:=====: 3
    7.   #####连续存根方式2:#####: 1
    8.   #####连续存根方式2:#####: 2
    9.   #####连续存根方式2:#####: 3
    10.   #####连续存根方式2:#####: 3
    11.   #####连续存根方式2:#####: 3
    12.   java.lang.RuntimeException
    13.   java.lang.NullPointerException
    复制代码
    启用 Mockito 的注解功能
      @RunWith(MockitoJUnitRunner.class) 开启注解功能
      使用 @RunWith(MockitoJUnitRunner.class) 在类上开启Mockito注解功能。
    1.  @RunWith(MockitoJUnitRunner.class)
    2.   public class MockitoAnnotationStartup {
    3.   }
    复制代码
     @Mock注解
      通过 @Mock注解可以得到mock对象,等价于 Mockito.mock(class)。
    1.     /**注解得到的mock对象*/
    2.       @Mock
    3.       List<String> mockList;
    4.      
    5.       等价于
    6.      
    7.       List<String> mock = Mockito.mock(List.class);
    复制代码
    示例如下:
    1. package org.byron4j.cookbook.mocketio.basic;
    2.   import org.junit.Test;
    3.   import org.mockito.Mock;
    4.   import org.mockito.Mockito;
    5.   import java.util.List;
    6.   import static org.junit.Assert.assertEquals;
    7.   public class MockitoAnnoTest extends MockitoAnnotationStartup{
    8.       /**注解得到的mock对象*/
    9.       @Mock
    10.       List<String> mockList;
    11.       @Test
    12.       public void testRaw(){
    13.           List<String> mock = Mockito.mock(List.class);
    14.           mock.add("one");
    15.           mock.add("one");
    16.           Mockito.verify(mock, Mockito.times(2)).add("one");
    17.           Mockito.when(mock.size()).thenReturn(100);
    18.           assertEquals(100, mock.size());
    19.       }
    20.       @Test
    21.       public void testAnno(){
    22.           mockList.add("one");
    23.           mockList.add("one");
    24.           Mockito.verify(mockList, Mockito.times(2)).add("one");
    25.           Mockito.when(mockList.size()).thenReturn(100);
    26.           assertEquals(100, mockList.size());
    27.       }
    28.   }
    复制代码
    @spy 注解
      和 @Mock 注解类似,还有 @Spy 注解。spy是密探间谍的意思,假冒的。
    1.  List<String> mock = Mockito.spy(List.class);
    复制代码
    使用注解:
    1. @Spy
    2.   List<String> spyList;
    复制代码
     @Captor 注解(参数捕获器)
      参数捕获器 ArgumentCaptor 对应注解 @Captor。
      原始方式创建一个参数捕获器:
    1. @Test
    2.   public void whenNotUseCaptorAnnotation_thenCorrect() {
    3.       List mockList = Mockito.mock(List.class);
    4.       ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
    5.   
    6.       mockList.add("one");
    7.       Mockito.verify(mockList).add(arg.capture());
    8.   
    9.       assertEquals("one", arg.getValue());
    10.   }
    复制代码
    使用@Captor注解创建一个参数捕获器:
    1. @Mock
    2.   List mockedList;
    3.   
    4.   @Captor
    5.   ArgumentCaptor argCaptor;
    6.   
    7.   @Test
    8.   public void whenUseCaptorAnnotation_thenTheSam() {
    9.       mockedList.add("one");
    10.       Mockito.verify(mockedList).add(argCaptor.capture());
    11.   
    12.       assertEquals("one", argCaptor.getValue());
    13.   }
    复制代码
     ·ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class); : 创建一个参数捕获器
      · Mockito.verify(mockedList).add(argCaptor.capture()); : 在一个验证中使用捕获器捕获方法add的参数; capture 方法必须在一个验证中。
      · argCaptor.getValue() : 获取参数捕获器捕获到的参数
      @InjectMocks 注解
      @InjectMocks 注解可以将mock的属性自动注入到测试对象中。
    1. @Mock
    2.   Map<String, String> wordMap;
    3.   @InjectMocks
    4.   MyDictionary myDictionary = new MyDictionary();
    5.   @Test
    6.       public void testInjectMocks(){
    7.           Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
    8.           assertEquals("aMeaning", myDictionary.getMeaning("aWord"));
    9.           System.out.println(myDictionary.getMeaning("aWord"));
    10.       }
    11.       class MyDictionary{
    12.           Map<String, String> wordMap;
    13.           public String getMeaning(String word){
    14.               return wordMap.get(word);
    15.           }
    16.       }
    复制代码
     ·MyDictionary 类存在属性 wordMap : Map<String, String> wordMap;
      · Mock 一个变量名为 wordMap :
    1.  @Mock
    2.   Map<String, String> wordMap;
    复制代码
    · 使用 @InjectMocks 注解标记 :
    1.  @InjectMocks
    2.   MyDictionary myDictionary = new MyDictionary();
    复制代码
    则会将mock出来的对象wordMap注入到myDictionary实例的同名属性中。

      注意事项:
      ·使用注解最小化重复编写创建mock对象的代码。
      · 使用注解让测试案例可读性更好。
      · 使用 @InjectMocks 注解注入 @Spy 和 @Mock 对象。
















    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-14 14:35 , Processed in 0.070740 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表