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

发表于:2023-3-06 09:32

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

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

  前言
  PowerMock是一个单元测试打桩框架,可以模拟静态方法,私有方法和final方法等来简化单元测试的编写。本篇文章将结合简单例子对PowerMock的常用方法进行说明。
  准备工作
  一. 注解添加与使用场景
  在使用PowerMock时需要针对不同场景添加对应注解,主要是@RunWith和@PrepareForTest注解。注解添加和场景对应如下所示。
  @PrepareForTest注解用于告诉PowerMock需要准备某些类进行测试,这些类包括final类,带有final,private,static或native方法的类,上述类需要PowerMock基于字节码进行操作。可以按照@PrepareForTest(TestObj.class) 的方式来告诉PowerMock准备TestObj类进行测试。
  @RunWith注解用于告诉JUnit使用哪个运行类来运行@RunWith注解修饰的类中的测试程序,例如@RunWith(PowerMockRunner.class),则告诉JUnit使用PowerMockRunner来运行@RunWith注解修饰的类中的测试程序。
  二. 使用PowerMock需要添加的依赖
  需要引入的依赖如下所示。
  <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>2.23.0</version>
      <scope>test</scope>
  </dependency>
  <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito2</artifactId>
      <version>2.0.2</version>
      <scope>test</scope>
  </dependency>
  <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>2.0.2</version>
      <scope>test</scope>
  </dependency>
  引入mockito-core是为了提供Mockito功能,主要使用到org.mockito.ArgumentMatchers参数占位符,部分情况需要使用到org.mockito.BDDMockito。引入powermock-api-mockito2和powermock-module-junit4是为了提供PowerMock功能,其中powermock-module-junit4中还引入了hamcrest-core,主要是使用其提供的org.hamcrest.MatcherAssert.assertThat和org.hamcrest.Matchers.is进行断言判断。
  在引入依赖时,需要注意核对Mockito和PowerMock的版本对应关系,否则会报java.lang.ClassNotFoundException: org.mockito.exceptions.Reporter错误。版本对应关系可以去PowerMock官网进行查询:PowerMock官网,通常情况下,如果引入的mockito-core版本为2.x,则PowerMock的api需要使用powermock-api-mockito2。
  正文
  一. Mock公共方法
  如下是一个有一个公共方法的待Mock类。
  public class MockPublicMethod {
      public boolean isTrue() {
          return true;
      }
  }
  如下是使用PowerMock对MockPublicMethod进行打桩,让MockPublicMethod#isTrue方法返回false。
  public class PowerMockTest {
      @Test
      public void mockPublic() {
          MockPublicMethod mockPublicMethod = PowerMockito
                  .mock(MockPublicMethod.class);
          PowerMockito.when(mockPublicMethod.isTrue()).thenReturn(false);
          assertThat(mockPublicMethod.isTrue(), is(false));
      }
  }
  Mock公共方法时需要使用PowerMockito.mock(方法所在类.class) 获取Mock出来的对象,这里称之为mock实例,mock实例的方法均为假方法,不对mock实例进行任何操作的情况下,调用mock实例的方法会返回(如果有返回值的话)返回值类型的默认值(零值,比如String返回null,Integer返回0)。如果想要调用mock实例的方法时使其执行真实方法,那么打桩时需要使用thenCallRealMethod(),如下所示。
  public class MockPublicMethod {
      public boolean isTrue() {
          return true;
      }
  }
  public class PowerMockTest {
      @Test
      public void mockPublic() {
          MockPublicMethod mockPublicMethod = PowerMockito
                  .mock(MockPublicMethod.class);
          PowerMockito.when(mockPublicMethod.isTrue()).thenCallRealMethod();
          assertThat(mockPublicMethod.isTrue(), is(true));
      }
  }
  同时可以使用whenNew() 来实现在程序中new一个对象时得到一个mock实例。如下所示。
  public class MockPublicMethod {
      public boolean isTrue() {
          return true;
      }
  }
  public class TestObj {
      public boolean isTrue() {
          MockPublicMethod mockPublicMethod = new MockPublicMethod();
          return mockPublicMethod.isTrue();
      }
  }
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(TestObj.class)
  public class PowerMockTest {
      @Test
      public void mockWhenNew() throws Exception {
          MockPublicMethod mockPublicMethod = PowerMockito.mock(MockPublicMethod.class);
          PowerMockito.when(mockPublicMethod.isTrue()).thenReturn(false);
          PowerMockito.whenNew(MockPublicMethod.class).withAnyArguments()
                  .thenReturn(mockPublicMethod);
          TestObj testObj = new TestObj();
          assertThat(testObj.isTrue(), is(false));
      }
  }
  特别注意:在使用whenNew() 时,@PrepareForTest注解中一定得是被测试类的Class对象。上面例子中,在被测试类TestObj的方法中new了一个Mock类的对象,所以在@PrepareForTest注解中加入了TestObj.class,此时whenNew() 才会在TestObj中生效。
  二. Mock Final公共方法
  对final公共方法进行Mock,基本与Mock公共方法一致,不过由于需要对final公共方法进行字节码操作以及需要使用PowerMockRunner来运行测试程序,因此需要在测试类上添加@RunWith和@PrepareForTest注解。
  如下是被测试类。
  public class MockFinnalPublicMethod {
      public boolean isTrue() {
          return true;
      }
  }
  测试类如下所示。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(MockFinnalPublicMethod.class)
  public class PowerMockTest {
      @Test
      public void mockFinalPublic() {
          MockFinnalPublicMethod mockFinnalPublicMethod = PowerMockito
                  .mock(MockFinnalPublicMethod.class);
          PowerMockito.when(mockFinnalPublicMethod.isTrue()).thenReturn(false);
          assertThat(mockFinnalPublicMethod.isTrue(), is(false));
      }
  }
  三. Mock私有方法
  被测试类如下所示。
  public class MockPrivateMethod {
      public boolean isTrue() {
          return returnTrue();
      }
      private boolean returnTrue() {
          return true;
      }
  }
  被测试类中有一个公共方法isTrue(),在isTrue() 方法中会调用MockPrivateMethod的私有方法returnTrue()。测试类如下所示。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(MockPrivateMethod.class)
  public class PowerMockTest {
      @Test
      public void mockPrivate() throws Exception {
          MockPrivateMethod mockPrivateMethod = PowerMockito.mock(MockPrivateMethod.class);
          PowerMockito.when(mockPrivateMethod, "returnTrue").thenReturn(false);
          PowerMockito.when(mockPrivateMethod.isTrue()).thenCallRealMethod();
          assertThat(mockPrivateMethod.isTrue(), is(false));
      }
  }
  Mock私有方法打桩时,需要使用PowerMockito.when(mock实例, "私有方法名").thenReturn(期望返回值) 的形式设置mock实例的私有方法的返回值,如果私有方法有参数,还需要在私有方法名后面添加参数占位符,比如PowerMockito.when(mock实例, "私有方法名", anyInt()).thenReturn(期望返回值)。上面例子中进行断言时,调用私有方法采取了调用公共方法来间接调用私有方法的形式,单元测试代码对业务代码造成了入侵,因此如果仅仅只是为了验证一个私有方法,可以使用Whitebox来方便的调用私有方法,如下所示。
  public class MockPrivateMethod {
      private boolean returnTrue() {
          return true;
      }
  }
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(MockPrivateMethod.class)
  public class PowerMockTest {
      @Test
      public void mockPrivate() throws Exception {
          MockPrivateMethod mockPrivateMethod = PowerMockito.mock(MockPrivateMethod.class);
          PowerMockito.when(mockPrivateMethod, "returnTrue").thenReturn(false);
          assertThat(Whitebox.invokeMethod(mockPrivateMethod, "returnTrue"),
                  is(false));
      }
  }
  四. Mock静态公共方法
  如下是被测试类,有一个静态公共方法,如下所示。
  public class MockStaticPublicMethod {
      public static boolean isTrue() {
          return true;
      }
  }
  测试类如下所示。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(MockStaticPublicMethod.class)
  public class PowerMockTest {
      @Test
      public void mockStaticPublic() {
          PowerMockito.mockStatic(MockStaticPublicMethod.class);
          PowerMockito.when(MockStaticPublicMethod.isTrue()).thenReturn(false);
          assertThat(MockStaticPublicMethod.isTrue(), is(false));
      }
  }
  对静态方法Mock时,首先需要在测试类上添加@RunWith和@PrepareForTest注解,同时需要调用PowerMockito的mockStatic() 方法完成对静态方法所在类的Mock,后续才能通过PowerMockito改变静态方法的行为。
  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

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

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

沪ICP备05003035号

沪公网安备 31010102002173号