PowerMockito mock静态类

1. Mock简介

一个大的项目中,类之间往往是相互依赖的,A类可能依赖B类,B类依赖C类。。。但是我们现在只想测试A类中的方法,如果不用Mock,就需要准备所有其他依赖类,并保证其他依赖类是正常工作的,非常麻烦。但是现在我们可以通过Mock造一个假的正确的B类结果出来,这样就将测试限制在对A类本身中,无需考虑其他依赖类。

SpringBoot的单元测试,可以使用mockito进行mock。但是mockito无法mock static、final这样的方法,因此诞生了PowerMockito。

2.PowerMockito依赖引入

由于PowerMock是Mockito的增强,所以无需额外引用Mockito,而PowerMock依赖需要两个:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>RELEASE</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>RELEASE</version>
    <scope>test</scope>
</dependency>

3.与Mockito不同的地方

PowerMockito里面封装了Mockito,所以使用PoweMockito时,也可以使用mockito的方法。@InjectMock和**@Mock**这些都是一样的,但是有一些地方需要改变下:

(1)单元测试测试类上的@RunWith(SpringRunner.class)里面的SpringRunner换成PowerMockRunner

注意,换成PowerMockRunner后,同一个类下的各个测试不再相互独立,它们公用一套环境,而不是每个测试起自己的环境,要注意是否存在对共有数据的修改。

@RunWith(PowerMockRunner.class)

(2)如果需要注入带有静态方法的类,类上加@PrepareForTest({XXX.class,XXX.class})

@PrepareForTest(RedisUtil.class)

注意:当需要mock系统类的静态方法的时候,必须加注解@PrepareForTest和@RunWith。注解里写的类不是系统类,而是调用系统类的类(一般是待测试类)。

且写在@PrepareForTest 里的类不会被 Jacoco 收集到代码覆盖率!!!

4.单测的写法

(1)mock有返回值的静态方法

mockStatic(XXX.class);
when(XXX.staticMethod(anyString(), anyString())).thenReturn(XX);

(2)mock void的静态方法

mockStatic(XXX.class);
doNothing().when(XXX.class, "voidStaticMethod", any(), any(), any());

(3)mock私有方法

mockPrivateClass = PowerMockito.spy(new MockPrivateClass());
PowerMockito.when(mockPrivateClass, "privateFunc").thenReturn("test");

注意:如果出现下面这个错误

java.lang.LinkageError: loader constraint violation: loader (instance of org/powermock/core/classloader/javassist/JavassistMockClassLoader) previously initiated loading for a different type with name “javax/management/MBeanServer”

只需要在测试类上加以下注解即可(在类上加,仅在某个test上加无效)

@PowerMockIgnore("javax.management.*")

5.实现部分方法Mock
通过@mock标注的类的所有方法都会被mock,不会去执行实际的方法;而通过@InjectMock标注的类是一个注入mock对象的类,待测方法一般位于该类中,它的方法不会被mock掉。如果我们想mock一个@InjectMock类中的方法该怎么办?这时就可以使用部分方法mock。在Mockito和PowerMockito(这两个在这里是完全一样的)中有两种方式实现部分方法mock ,一种使用mock,一种使用spy。

(1) mock实现
使用mock,默认都是不执行实际方法,直接返回设定的返回值;但是如果要执行实际方法,则需要使用doCallRealMethod()或thenCallRealMethod():

//假设TestService在全局是@InjectMock,这里单独重新声明为一个mock类
TestService testService = Mockito.mock(TestService.class); //这个与@Mock作用一样,但是这个不会被注入到InjectMock类中
//不调用实际方法,即不进入该方法中
testService.testMethod(any());
//2种调用实际方法的声明(Mockito和PowerMockito写法一样)
doCallRealMethod().when(testService).testMethod(any());
when(testService.testMethod(any())).thenCallRealMethod();
//PowerMockito的另外两种写法
PowerMockito.doCallRealMethod().when(testService, "testMethod", any());
PowerMockito.when(testService, "testMethod", any()).thenCallRealMethod();

(2)spy实现
使用spy,thenReturn默认是执行实际方法的,只是返回值可以按照设定的进行返回;如果想要不执行实际方法,需要使用doReturn:

TestService testService = Mockito.spy(new TestService());
//2种会执行实际方法
testService.testMethod(any());
when(testService.testMethod(any())).thenReturn("hello");
//不执行实际方法
doReturn("hello").when(testService).testMethod(any()); //有返回值
doNothing().when(testService).testMethod(any()); //无返回值

6.部分方法mock时测试方法还引入了其他service时

当我们通过上面的Mockito.mock(TestService.class)创造了一个新的testService时,该service默认是没有注入任何mock类的,如果测试方法中调用了其他service的方法,那么这个service就会报空指针的问题(因为没有注入)。为解决这个问题,就得重新Mock一个其他类,并通过 PowerMockito.field 注入进测试类中。

PowerMockito.field本是用来mock私有变量的方法,但由于@Autowired标记的一般都是private变量,所以这里通过该方法进行注入。

class TestService {
	 @Autowierd
	 private OtherService otherService;
 
 	 public String testMethod() {
 		......
 		String ret = otherService.otherMethod();
 		......
	 }
}
TestService testService = PowerMockito.mock(TestService.class);
OtherService otherService = PowerMockito.mock(OtherService.class);

PowerMockito.when(testService.testMethod(any()).thenCallRealMethod();
PowerMockito.field(TestService.class, "otherService").set(testService, otherService);
PowerMockito.when(otherService.otherMathod(any())).thenReturn("xxx");

总之:一般在测试某个类时,该类中被@Autowired注解的Bean都会在测试类中手动通过@Mock注入到InjectMock类中,因此不会出现空指针问题(即找不到该类)。但是通过 PowerMockito.mock() 创建的mock类中没有注入任何其他service,因此要警惕空指针情况(提前通过 PowerMockito.field 进行set)。

7.注意 NullPointerException: can’t unbox null value 问题

有时候测试时懒得造太多数据,给参数设为null,出现了NullPointerException: can’t unbox null value问题。这个问题不注意的话会以为是调用者为null,但其实是调用的方法中实参是null,形参是基本类型,导致出现拆箱问题。(其实就是不能出现 int a = null 这样的问题)

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: PowerMockito是一个用于Java单元测试的工具,它可以用来测试私有方法。使用PowerMockito的方法是在测试类上使用@PowerMock注解,并在测试方法中使用PowerMockito.spy()来创建被测试类的spy对象,然后使用PowerMockito.when()来指定私有方法的返回值或执行其他操作。 例如: ``` @RunWith(PowerMockRunner.class) @PrepareForTest(MyClass.class) public class MyClassTest { @Test public void testPrivateMethod() throws Exception { MyClass myClass = PowerMockito.spy(new MyClass()); PowerMockito.when(myClass, "privateMethod").thenReturn("Hello PowerMockito"); String result = myClass.publicMethod(); assertEquals("Hello PowerMockito", result); } } ``` 请注意,使用PowerMockito进行单元测试可能会使测试变得复杂和难以维护,因此在使用PowerMockito之前需要谨慎考虑。 ### 回答2: PowerMockito 是一个基于 Mockito 和 JUnit 的 Java 测试框架,它可以用于测试那些无法使用传统单元测试框架测试的代码,如静态方法、私有方法、构造函数等等。 测试私有方法可以用 PowerMockito 来实现,主要分为 3 个步骤: 1. 引入 PowerMockito 首先需要引入 PowerMockito 库,它需要依赖 Mockito 和 JUnit。可以通过 Maven 或 Gradle 来引入。 ```xml <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-core</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.7.7</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> ``` 2. 使用 PowerMockito.mock 方法来创建被测试对象 使用 PowerMockito.mock 方法来创建被测试对象,然后再对其私有方法进行测试: ```java public class TestClass { private String privateMethod() { return "Hello, World!"; } } @RunWith(PowerMockRunner.class) @PrepareForTest(TestClass.class) public class TestClassTest { @Test public void testPrivateMethod() throws Exception { TestClass testClass = PowerMockito.mock(TestClass.class); PowerMockito.when(testClass, "privateMethod").thenReturn("Hello, PowerMockito!"); assertEquals("Hello, PowerMockito!", Whitebox.invokeMethod(testClass, "privateMethod")); } } ``` 在这个示例中,我们使用了 @PrepareForTest 注解来告诉 PowerMockito 需要准备被测试的类。然后我们使用 PowerMockito.mock 方法来创建 TestClass 的一个模拟对象 testClass ,并使用 PowerMockito.when 方法来告诉 PowerMockito 当调用 privateMethod 方法时应该返回什么值。最后我们使用 Whitebox.invokeMethod 来调用模拟对象的私有方法。 3.使用 PowerMockito.spy 方法来实现部分模拟 有时我们需要对一个对象进行部分模拟(mock 部分方法,而保留其它方法的真实实现)。此时可以使用 PowerMockito.spy 方法来创建被测试对象。 ```java public class TestClass { private String privateMethod() { return "Hello, World!"; } public String publicMethod() { return "Hello, PowerMockito!"; } } @RunWith(PowerMockRunner.class) @PrepareForTest(TestClass.class) public class TestClassTest { @Test public void testPrivateMethod() throws Exception { TestClass testClass = PowerMockito.spy(new TestClass()); PowerMockito.when(testClass, "privateMethod").thenReturn("Hello, PowerMockito!"); assertEquals("Hello, PowerMockito!", Whitebox.invokeMethod(testClass, "privateMethod")); assertEquals("Hello, PowerMockito!", testClass.publicMethod()); } } ``` 在这个示例中,我们使用 PowerMockito.spy 方法来创建 TestClass 的一个继承自原始对象的部分模拟对象 testClass 。接下来我们使用 PowerMockito.when 方法来告诉 PowerMockito 当调用 privateMethod 方法时应该返回什么值。最后我们可以测试模拟对象的私有方法和公有方法。 ### 回答3: PowerMockito是一个mock testing框架,它可以帮助我们进行单元测试。当我们需要测试一个类的私有方法时,PowerMockito可以提供很好的解决方案。 为了测试私有方法,我们可以使用PowerMockito.whenNew()方法来模拟对象,并使用PowerMockito.spy()方法来创建一个对象的spy对象,然后使用这个对象来调用私有方法。 以下是使用PowerMockito测试私有方法的步骤: 1. 引入PowerMockito依赖:在项目的pom文件中添加以下依赖: ``` <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-core</artifactId> <version>2.0.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>2.0.0</version> <scope>test</scope> </dependency> ``` 2. 创建一个需要测试的类: ``` public class User { private String name; private int age; private String address; private String phoneNumber; private boolean isLegalAge(int age) { return age >= 18; } private boolean isValidPhone(String phoneNumber) { return phoneNumber.matches("^(?:(?:\\+|00)(\\d{1,3})[\\s-]?)?(\\d{8,14})$"); } public boolean isValidUser() { return isLegalAge(age) && isValidPhone(phoneNumber); } } ``` 3. 创建一个测试类,并使用PowerMockito来测试私有方法: ``` @RunWith(PowerMockRunner.class) @PrepareForTest(User.class) public class UserTest { @Test public void testIsValidPhone() throws Exception { User user = new User(); User spy = PowerMockito.spy(user); // 使用spy()方法创建spy对象 PowerMockito.doReturn(true).when(spy, "isValidPhone", "12345678901"); // 使用doReturn()方法模拟方法 assertTrue(spy.isValidUser()); } @Test public void testIsLegalAge() throws Exception { User user = new User(); User spy = PowerMockito.spy(user); // 使用spy()方法创建spy对象 PowerMockito.doReturn(false).when(spy, "isLegalAge", 15); // 使用doReturn()方法模拟方法 assertFalse(spy.isValidUser()); } } ``` 在以上代码中,我们使用spy()方法创建一个User类的spy对象,然后使用doReturn()方法模拟私有方法,并在测试方法中调用公共方法isValidUser()来触发私有方法的调用。 总结一下:PowerMockito提供了一种方便的方式来测试类中的私有方法。我们可以使用spy()方法来创建一个对象的spy对象,并使用doReturn()方法来模拟私有方法。这样我们就可以在不暴露私有方法的情况下,进行单元测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值