使用PowerMock进行单元测试(2)

发表于:2023-3-07 09:20

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:半夏之沫    来源:稀土掘金

  五. Mock静态私有方法
  被测试类如下所示。
  public class MockStaticPrivateMethod {
      public static boolean isTrue() {
          return returnTrue();
      }
      private static boolean returnTrue() {
          return true;
      }
  }
  被测试类中有一个静态公共方法isTrue(),在isTrue() 方法中会调用MockStaticPrivateMethod的静态私有方法returnTrue()。测试程序如下所示。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(MockStaticPrivateMethod.class)
  public class PowerMockTest {
      @Test
      public void mockStaticPrivate() throws Exception {
          PowerMockito.mockStatic(MockStaticPrivateMethod.class);
          PowerMockito.when(MockStaticPrivateMethod.class, "returnTrue")
                  .thenReturn(false);
          PowerMockito.when(MockStaticPrivateMethod.isTrue()).thenCallRealMethod();
          assertThat(MockStaticPrivateMethod.isTrue(), is(false));
      }
  }
  同样测试代码对业务代码造成了入侵,可以使用Whitebox来方便的调用静态私有方法,如下所示。
  public class MockStaticPrivateMethod {
      private static boolean returnTrue() {
          return true;
      }
  }
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(MockStaticPrivateMethod.class)
  public class PowerMockTest {
      @Test
      public void mockStaticPrivate() throws Exception {
          PowerMockito.mockStatic(MockStaticPrivateMethod.class);
          PowerMockito.when(MockStaticPrivateMethod.class, "returnTrue")
                  .thenReturn(false);
          assertThat(Whitebox.invokeMethod(MockStaticPrivateMethod.class, "returnTrue"),
                  is(false));
      }
  }
  六. Whitebox使用
  1. 设置对象私有字段
  使用Whitebox可以方便的设置对象(静态)私有字段值。被测试类如下所示。
  public class WhiteboxHelp {
      private boolean flag = true;
      public boolean isTrue() {
          return flag;
      }
  }
  被测试类WhiteboxHelp有一个私有字段flag,同时WhiteboxHelp的isTrue() 方法会返回flag的值。测试类如下所示。
  public class PowerMockTest {
      @Test
      public void whiteboxPrivateField() {
          WhiteboxHelp whiteboxHelp = new WhiteboxHelp();
          Whitebox.setInternalState(whiteboxHelp, "flag", false);
          assertThat(whiteboxHelp.isTrue(), is(false));
      }
  }
  仅使用Whitebox时不需要添加@RunWith和@PrepareForTest注解,同时对于上面例子如果flag是静态变量,那么设置静态变量值时需要使用Whitebox.setInternalState(WhiteboxHelp.class, "flag", false)。
  特别注意:如果WhiteboxHelp的flag字段是静态的,则无法使用Whitebox设置flag字段的值。
  2. 调用私有方法
  使用Whitebox也可以方便的调用对象(静态)私有方法。被测试类如下所示。
  public class WhiteboxHelp {
      private boolean isTrue() {
          return true;
      }
  }
  测试类如下所示。
  public class PowerMockTest {
      @Test
      public void whiteboxPrivateMethod() throws Exception {
          WhiteboxHelp whiteboxHelp = new WhiteboxHelp();
          assertThat(Whitebox.invokeMethod(whiteboxHelp, "isTrue"), is(true));
      }
  }
  对于上面例子,如果isTrue() 是静态私有方法,那么调用静态私有方法时的语句为:assertThat(Whitebox.invokeMethod(WhiteboxHelp.class, "isTrue"), is(true))。
  七. Answer-Mock
  针对同一方法多次被调用且不同入参需要Mock不同出参的情况,可以使用Answer。
  被测试类如下所示。
  public class AnswerHelp {
      public String convert(int num) {
          return StringUtils.EMPTY;
      }
  }
  测试类如下所示。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(AnswerHelp.class)
  public class PowerMockTest {
      @Test
      public void answer() {
          AnswerHelp answerHelp = PowerMockito.mock(AnswerHelp.class);
          Answer<String> answer = new Answer<String>() {
              @Override
              public String answer(InvocationOnMock invocation) {
                  int num = (Integer) invocation.getArguments()[0];
                  if (num == 0) {
                      return "zero";
                  } else if (num == 1) {
                      return "one";
                  }
                  return StringUtils.EMPTY;
              }
          };
          PowerMockito.when(answerHelp.convert(anyInt())).thenAnswer(answer);
          assertThat(answerHelp.convert(0), is("zero"));
          assertThat(answerHelp.convert(1), is("one"));
      }
  }
  其中Answer的泛型类型需要与answer() 方法的返回值类型一致,且通过InvocationOnMock的getArguments() 可以获取mock实例调用的方法所有入参,callRealMethod() 可以调用真实方法,getMethod() 可以获取mock实例调用的方法,getMock() 可以获取mock实例。同时,还可以使用org.mockito.BDDMockito.given来实现相同的效果,如下所示。
  public class AnswerHelp {
      public String convert(int num) {
          return StringUtils.EMPTY;
      }
  }
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(AnswerHelp.class)
  public class PowerMockTest {
      @Test
      public void answer() {
          AnswerHelp answerHelp = PowerMockito.mock(AnswerHelp.class);
          Answer<String> answer = new Answer<String>() {
              @Override
              public String answer(InvocationOnMock invocation) {
                  int num = (Integer) invocation.getArguments()[0];
                  if (num == 0) {
                      return "zero";
                  } else if (num == 1) {
                      return "one";
                  }
                  return StringUtils.EMPTY;
              }
          };
          given(answerHelp.convert(anyInt())).willAnswer(answer);
          assertThat(answerHelp.convert(0), is("zero"));
          assertThat(answerHelp.convert(1), is("one"));
      }
  }
  八. Spy公共方法
  先给出一个示例,再对Spy进行解释。被测试类如下所示。
  public class SpyPublicMethod {
      public boolean isTrue1() {
          return true;
      }
      public boolean isTrue2() {
          return true;
      }
  }
  测试类如下所示。
  public class PowerMockTest {
      @Test
      public void spyPublic() {
          SpyPublicMethod spy = PowerMockito.spy(new SpyPublicMethod());
          PowerMockito.doReturn(false).when(spy).isTrue1();
          assertThat(spy.isTrue1(), is(false));
          assertThat(spy.isTrue2(), is(true));
      }
  }
  Spy公共方法时需要使用PowerMockito.spy(方法所在类的实例) 获取Spy出来的对象,这里称之为spy实例,不对spy实例进行任何操作的情况下,spy实例与真实实例是完全一样的。同时由于spy实例与真实实例完全一样,因此在对spy实例进行打桩时使用doReturn() 和thenReturn() 是存在差别的:使用doReturn(返回值) 时不会执行真实方式,直接返回返回值;使用thenReturn(返回值) 时会先执行一遍真实方法,然后返回返回值。通常情况下Spy需要配合doReturn() 使用,用于抑制真实方法的执行,防止执行真实方法时报错。
  同时,打桩时使用doReturn() 和thenReturn() 的语法存在差别,上面例子中打桩时如果使用的语句为PowerMockito.doReturn(false).when(spy.isTrue1()),会导致编译时正常,运行时报错的现象。下表对打桩时doReturn() 和thenReturn() 的语法进行了对比。
  最后,Spy也和Mock一样,可以配合whenNew() 进行使用。
  九. Spy私有方法
  被测试类如下如下所示。
  public class SpyPrivateMethod {
      public boolean isTrue() {
          return returnTrue();
      }
      public boolean returnTrue() {
          return true;
      }
  }
  测试类如下所示。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(SpyPrivateMethod.class)
  public class PowerMockTest {
      @Test
      public void spyPrivate() throws Exception {
          SpyPrivateMethod spyPrivateMethod = PowerMockito.spy(new SpyPrivateMethod());
          PowerMockito.doReturn(false).when(spyPrivateMethod, "returnTrue");
          assertThat(spyPrivateMethod.isTrue(), is(false));
      }
  }
  十. Spy静态方法
  1. Spy静态公共方法
  被测试类如下所示。
  public class SpyStaticPublicMethod {
      public static boolean isTrue() {
          return true;
      }
  }
  测试类如下所示。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(SpyStaticPublicMethod.class)
  public class PowerMockTest {
      @Test
      public void spyStaticPublic() throws Exception {
          PowerMockito.spy(SpyStaticPublicMethod.class);
          PowerMockito.doReturn(false).when(SpyStaticPublicMethod.class, "isTrue");
          assertThat(SpyStaticPublicMethod.isTrue(), is(false));
      }
  }
  2. Spy静态私有方法
  被测试类如下所示。
  public class SpyStaticPrivateMethod {
      public static boolean isTrue() {
          return returnTrue();
      }
      private static boolean returnTrue() {
          return true;
      }
  }
  测试类如下所示。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(SpyStaticPrivateMethod.class)
  public class PowerMockTest {
      @Test
      public void spyStaticPrivate() throws Exception {
          PowerMockito.spy(SpyStaticPrivateMethod.class);
          PowerMockito.doReturn(false).when(SpyStaticPrivateMethod.class, "returnTrue");
          assertThat(SpyStaticPrivateMethod.isTrue(), is(false));
      }
  }
  十一. Answer-Spy
  被测试类如下所示。
  public class AnswerHelp {
      public String convert(int num) {
          return StringUtils.EMPTY;
      }
  }
  测试类如下所示。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(AnswerHelp.class)
  public class PowerMockTest {
      @Test
      public void answer() {
          AnswerHelp answerHelp = PowerMockito.spy(new AnswerHelp());
          Answer<String> answer = new Answer<String>() {
              @Override
              public String answer(InvocationOnMock invocation) {
                  int num = (Integer) invocation.getArguments()[0];
                  if (num == 0) {
                      return "zero";
                  } else if (num == 1) {
                      return "one";
                  }
                  return StringUtils.EMPTY;
              }
          };
          PowerMockito.doAnswer(answer).when(answerHelp).convert(anyInt());
          assertThat(answerHelp.convert(0), is("zero"));
          assertThat(answerHelp.convert(1), is("one"));
      }
      @Test
      public void given_when_then_bdd() {
          AnswerHelp answerHelp = PowerMockito.spy(new AnswerHelp());
          Answer<String> answer = new Answer<String>() {
              @Override
              public String answer(InvocationOnMock invocation) {
                  int num = (Integer) invocation.getArguments()[0];
                  if (num == 0) {
                      return "zero";
                  } else if (num == 1) {
                      return "one";
                  }
                  return StringUtils.EMPTY;
              }
          };
          given(answerHelp.convert(anyInt())).willAnswer(answer);
          assertThat(answerHelp.convert(0), is("zero"));
          assertThat(answerHelp.convert(1), is("one"));
      }
  }
  总结
  合理使用PowerMock可以处理单元测试编写中的一些常见难题,在解依赖时可以帮助我们规避例如数据库,kafka等与外部存在交互的组件的影响。
  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号