51Testing软件测试论坛

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

QQ登录

只需一步,快速开始

微信登录,快人一步

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

[原创] 分享Mockito单元测试利器使用方式(二)

[复制链接]
  • TA的每日心情
    无聊
    4 小时前
  • 签到天数: 937 天

    连续签到: 4 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2023-3-24 13:13:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    6、使用注解简化 mock 对象创建
      注意!下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者 test runner 中:
      MockitoAnnotations.initMocks(this);

      也可以使用内置的 runner: MockitoJUnitRunner 或者一个 rule : MockitoRule;
      // 代替 mock(ArgumentTestService.class) 创建mock对象;
      @Mock
      private ArgumentTestService argumentTestService;
      // 若改注解修饰的对象有成员变量,@Mock定义的mock对象会被自动注入;
      @InjectMocks
      private MockitoAnnotationServiceImpl mockitoAnnotationService;
      @Test
      public void test6() {
          // 注意!下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者test runner中;
          MockitoAnnotations.initMocks(this);
          when(argumentTestService.argumentTestMethod(new ArgumentTestRequest())).thenReturn("success");
          System.out.println(argumentTestService.argumentTestMethod(new ArgumentTestRequest()));  //success
          System.out.println(mockitoAnnotationService.mockitoAnnotationTestMethod()); //null
      }


      7、监控真实对象(部分 mock)
      ·可以为真实对象创建一个监控 (spy) 对象。当你使用这个 spy 对象时真实的对象也会也调用,除非它的函数被 stub 了;
      · 尽量少使用 spy 对象,使用时也需要小心形式,例如 spy 对象可以用来处理遗留代码;
      · stub 语法中同样提供了部分 mock 的方法,可以调用真实的方法;
      完全 mock:
      上文讲的内容是完全 mock,即创建的 mock 对象与真实对象无关,mock 对象的方法默认都是基本的实现,返回基本类型。可基于接口、实现类创建 mock 对象。
      部分 mock:
      所谓部分 mock,即创建的 mock 对象时基于真实对象的,mock 对象的方法都是默认使用真实对象的方法,除非 stub 之后,才会以 stub 为准。基于实现类创建 mock 对象,否则在没有 stub 的情况下,调用真实方法时,会出现异常。
      注意点:
      Mockito 并不会为真实对象代理函数调用,实际上它会拷贝真实对象。因此如果你保留了真实对象并且与之交互,不要期望从监控对象得到正确的结果。 当你在监控对象上调用一个没有被 stub 的函数时并不会调用真实对象的对应函数,你不会在真实对象上看到任何效果
      @Test
      public void test7() {
          // stub部分mock(stub中使用真实调用)。注意:需要mock实现类,否则会有异常
          final StubTestService stubTestService = mock(StubTestServiceImpl.class);
          when(stubTestService.stubTestMethodA("paramA")).thenCallRealMethod();
          doCallRealMethod().when(stubTestService).stubTestMethodB();
          System.out.println(stubTestService.stubTestMethodA("paramA"));  //stubTestMethodA is called, param = paramA
          System.out.println(stubTestService.stubTestMethodB());  //stubTestMethodB is called
          System.out.println(stubTestService.stubTestMethodC());  //null
          // spy部分mock
          final LinkedList<String> linkedList = new LinkedList();
          final LinkedList spy = spy(linkedList);
          spy.add("one");
          spy.add("two");
          doReturn(100).when(spy).size();
          when(spy.get(0)).thenReturn("one_test");
          System.out.println(spy.size()); //100
          System.out.println(spy.get(0)); //one_test
          System.out.println(spy.get(1)); //two
          // spy可以类比AOP。在spy中,由于默认是调用真实方法,所以第二种写法不等价于第一种写法,不推荐这种写法。
          doReturn("two_test").when(spy).get(2);
          when(spy.get(2)).thenReturn("two_test"); //异常 java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
          System.out.println(spy.get(2));   //two_test
          // spy对象只是真实对象的复制,真实对象的改变不会影响spy对象
          final List<String> arrayList = new ArrayList<>();
          final List<String> spy1 = spy(arrayList);
          spy1.add(0, "one");
          System.out.println(spy1.get(0));    //one
          arrayList.add(0, "list1");
          System.out.println(arrayList.get(0));   //list1
          System.out.println(spy1.get(0));    //one
          // 若对某个方法stub之后,又想调用真实的方法,可以使用reset(spy)
          final ArrayList<String> arrayList1 = new ArrayList<>();
          final ArrayList<String> spy2 = spy(arrayList1);
          doReturn(100).when(spy2).size();
          System.out.println(spy2.size());    //100
          reset(spy2);
          System.out.println(spy2.size());    //0
      }


      8、@Mock 和 @spy 的使用
      ·@Mock 等价于 Mockito.mock (Object.class);
      · @Spy 等价于 Mockito.spy (obj);
      区分是mock对象还是spy对象:
      Mockito.mockingDetails(someObject).isMock();
      Mockito.mockingDetails(someObject).isSpy();
      @Mock
      private StubTestService stubTestService;
      @Spy
      private StubTestServiceImpl stubTestServiceImpl;
      @Spy
      private StubTestService stubTestServiceImpl1 = new StubTestServiceImpl();
      @Test
      public void test8() {
          MockitoAnnotations.initMocks(this);
          // mock对象返回默认
          System.out.println(stubTestService.stubTestMethodB());  //null
          // spy对象调用真实方法
          System.out.println(stubTestServiceImpl.stubTestMethodC());  //stubTestMethodC is called
          System.out.println(stubTestServiceImpl1.stubTestMethodA("spy"));  //stubTestMethodA is called, param = spy
          // 区分是mock对象还是spy对象
          System.out.println(mockingDetails(stubTestService).isMock());   //true
          System.out.println(mockingDetails(stubTestService).isSpy());    //false
          System.out.println(mockingDetails(stubTestServiceImpl).isSpy());    //true
      }


      9、ArgumentCaptor(参数捕获器)捕获方法参数进行验证。(可代替参数匹配器使用)
      在某些场景中,不光要对方法的返回值和调用进行验证,同时需要验证一系列交互后所传入方法的参数。那么我们可以用参数捕获器来捕获传入方法的参数进行验证,看它是否符合我们的要求。
      ArgumentCaptor 介绍
      通过 ArgumentCaptor 对象的 forClass (Class
      ArgumentCaptor 的 Api
      argument.capture () 捕获方法参数
      argument.getValue () 获取方法参数值,如果方法进行了多次调用,它将返回最后一个参数值
      argument.getAllValues () 方法进行多次调用后,返回多个参数值
      @Test
      public void test9() {
          List mock = mock(List.class);
          List mock1 = mock(List.class);
          mock.add("John");
          mock1.add("Brian");
          mock1.add("Jim");
          // 获取方法参数
          ArgumentCaptor argument = ArgumentCaptor.forClass(String.class);
          verify(mock).add(argument.capture());
          System.out.println(argument.getValue());    //John
          // 多次调用获取最后一次
          ArgumentCaptor argument1 = ArgumentCaptor.forClass(String.class);
          verify(mock1, times(2)).add(argument1.capture());
          System.out.println(argument1.getValue());    //Jim
          // 获取所有调用参数
          System.out.println(argument1.getAllValues());    //[Brian, Jim]
      }


      10、简化 ArgumentCaptor 的创建
      @Mock
      private List<String> captorList;
      @Captor
      private ArgumentCaptor<String> argumentCaptor;
      @Test
      public void test10() {
          MockitoAnnotations.initMocks(this);
          captorList.add("cap1");
          captorList.add("cap2");
          System.out.println(captorList.size());
          verify(captorList, atLeastOnce()).add(argumentCaptor.capture());
          System.out.println(argumentCaptor.getAllValues());
      }


      11、高级特性:自定义验证失败信息
      @Test
      public void test11() {
          final ArrayList arrayList = mock(ArrayList.class);
          arrayList.add("one");
          arrayList.add("two");
          verify(arrayList, description("size()没有调用")).size();
          // org.mockito.exceptions.base.MockitoAssertionError: size()没有调用
          verify(arrayList, timeout(200).times(3).description("验证失败")).add(anyString());
          //org.mockito.exceptions.base.MockitoAssertionError: 验证失败
      }


      12、高级特性:修改没有测试桩的调用的默认返回值
      ·可以指定策略来创建 mock 对象的返回值。这是一个高级特性,通常来说,你不需要写这样的测试;
      · 它对于遗留系统来说是很有用处的。当你不需要为函数调用打桩时你可以指定一个默认的 answer;
      @Test
      public void test12(){
          // 创建mock对象、使用默认返回
          final ArrayList mockList = mock(ArrayList.class);
          System.out.println(mockList.get(0));    //null
          // 这个实现首先尝试全局配置,如果没有全局配置就会使用默认的回答,它返回0,空集合,null,等等。
          // 参考返回配置:ReturnsEmptyValues
          mock(ArrayList.class, Answers.RETURNS_DEFAULTS);
          // ReturnsSmartNulls首先尝试返回普通值(0,空集合,空字符串,等等)然后它试图返回SmartNull。
          // 如果最终返回对象,那么会简单返回null。一般用在处理遗留代码。
          // 参考返回配置:ReturnsMoreEmptyValues
          mock(ArrayList.class, Answers.RETURNS_SMART_NULLS);
          // 未stub的方法,会调用真实方法。
          //    注1:存根部分模拟使用时(mock.getSomething ()) .thenReturn (fakeValue)语法将调用的方法。对于部分模拟推荐使用doReturn语法。
          //    注2:如果模拟是序列化反序列化,那么这个Answer将无法理解泛型的元数据。
          mock(ArrayList.class, Answers.CALLS_REAL_METHODS);
          // 深度stub,用于嵌套对象的mock。参考:https://www.cnblogs.com/Ming8006/p/6297333.html
          mock(ArrayList.class, Answers.RETURNS_DEEP_STUBS);
          // ReturnsMocks首先尝试返回普通值(0,空集合,空字符串,等等)然后它试图返回mock。
          // 如果返回类型不能mocked(例如是final)然后返回null。
          mock(ArrayList.class, Answers.RETURNS_MOCKS);
          //  mock对象的方法调用后,可以返回自己(类似builder模式)
          mock(ArrayList.class, Answers.RETURNS_SELF);
          // 自定义返回
          final Answer<String> answer = new Answer<String>() {
              @Override
              public String answer(InvocationOnMock invocation) throws Throwable {
                  return "test_answer";
              }
          };
          final ArrayList mockList1 = mock(ArrayList.class, answer);
          System.out.println(mockList1.get(0));   //test_answer
      }



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

    使用道具 举报

    本版积分规则

    关闭

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

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

    GMT+8, 2024-4-25 13:49 , Processed in 0.063090 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

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