Java单元测试实践-05.Mockito、PowerMock基本功能使用

Java单元测试实践-00.目录(9万多字文档+700多测试示例)
https://blog.csdn.net/a82514921/article/details/107969340

1. Mockito与PowerMock的功能

以下针对使用Mockito与PowerMock作为单元测试Mock框架进行说明。

1.1. Mockito

参考 https://github.com/mockito/mockito/wiki/FAQ 。

Mockito是一个Java的Mock框架。支持对类或接口进行Mock,不支持对静态方法、私有方法、构造函数等进行Mock。

1.2. PowerMock

参考 https://github.com/powermock/powermock/blob/release/2.x/README.md 。

PowerMock是一个Mock框架,以更强大的功能扩展了其他Mock库。PowerMock使用自定义的类加载器和字节码操作来对静态方法、构造函数、final类和方法、私有方法等进行Mock。PowerMock旨在通过少量方法和注解扩展现有的API,以启用额外的功能。目前,PowerMock支持EasyMock和Mockito。

PowerMock示例可查看: https://github.com/powermock/powermock-examples-maven 。

2. 添加引用

2.1. 引用Mockito

参考“Declaring mockito dependency”( https://github.com/mockito/mockito/wiki/Declaring-mockito-dependency )。

可通过以下方式引用Mockito:

"org.mockito:mockito-core:1.+"

Mockito版本已更新至2.*与3.*,可使用新版本。

2.2. 引用PowerMock

参考“Mockito 2 Maven”( https://github.com/powermock/powermock/wiki/Mockito-2-Maven )。

对于使用Mockito 2.x,JUnit 4.4及以上版本时,通过以下方式引用PowerMock:

"org.powermock:powermock-module-junit4:2.0.4",
"org.powermock:powermock-api-mockito2:2.0.4"

3. PowerMock对Mockito的支持版本

参考“Supported versions”( https://github.com/powermock/powermock/wiki/Mockito#supported-versions )。

PowerMock的2.0.0及更高版本支持Mockito 2。具体的版本支持关系可查看对应的表格。

4. 基本配置

在使用PowerMock时,通常会使用以下公共配置(@ContextConfiguration是spring-test的配置):

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
@PowerMockIgnore({"javax.management.*", "javax.net.ssl.*", "javax.crypto.*", "javax.security.*", "javax.script.*"})

4.1. @RunWith(PowerMockRunner.class)

参考“Introduction”( https://github.com/powermock/powermock/wiki/mockito#introduction )。

PowerMock提供了一个名为“PowerMockito”的类,用于创建模拟/对象/类并启动验证和期望。

所有的用法都需要在类级别使用@RunWith(PowerMockRunner.class)和@PrepareForTest注解。

4.2. @PrepareForTest注解

参考“Annotation Type PrepareForTest”( https://javadoc.io/doc/org.powermock/powermock-core/latest/org/powermock/core/classloader/annotations/PrepareForTest.html )。

@PrepareForTest注解告诉PowerMock需要准备哪些类进行测试。需要使用此注解定义的类通常是需要操作字节码的类。包括final类,具有final、private、static或native方法的类,以及应在实例化时返回模拟对象的类。

@PrepareForTest注解可以添加在测试类或单个测试方法上。如果设置在一个类上,则该测试类中的所有测试方法都将由PowerMock处理,以实现可测试性。如果需要覆盖单个方法的行为,只需在特定的测试方法上设置@PrepareForTest注解。例如,在测试方法f1()中修改类C但在测试方法f2()中希望保持C不变的情况下,可以在方法f2()上设置一个@PrepareForTest注解,并从value()列表中排除类C。

有时测试时需要使用@PrepareForTest注解对内部类进行准备,可以通过提供内部类的完整名称来完成,该名称应被添加到fullyQualifiedNames()列表中。

还可以使用通配符准备整个程序包以进行测试,示例如下:

@PrepareForTest(fullyQualifiedNames = "com.mypackage.*")

@PrepareForTest注解可以添加在测试类,或测试类的超类中,子类与超类中的配置不会相互覆盖。

例如在子类中通过@PrepareForTest注解指定了A.class,在超类中指定了B.class,则A.class与B.class均会被@PrepareForTest注解处理。

可参考示例TestPrepareForTestChild、TestPrepareForTestParent类。

4.3. @PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)

参考“JUnit_Delegating_Runner”( https://github.com/powermock/powermock/wiki/junit_delegating_runner )。

使用@PowerMockRunnerDelegate注解,从1.6.0版开始,PowerMock支持将测试执行委托给另一个JUnit运行程序,而无需使用JUnit规则。这会将实际的测试执行留给选择的其他runner。例如,测试可以委托给“SpringJUnit4ClassRunner”,“Parameterized”或“Enclosed”等runner。

4.4. @PowerMockIgnore

参考“Global @PowerMockIgnore”( https://github.com/powermock/powermock/wiki/PowerMock-Configuration#global-powermockignore )。

默认情况下,PowerMock使用其MockClassLoader加载所有类。该类加载器加载并修改了所有类,除了以下情况:

  • 系统类。它们被延期到系统类加载器
  • 位于指定为忽略的程序包中的类

可以使用@PowerMockIgnore注解指定需要忽略的包,避免被PowerMock加载。

例如在调用加解密相关代码时,需要通过@PowerMockIgnore({“javax.crypto.*”})忽略上述包,否则会出现以下异常( 使用RSA私钥加密 ):

java.lang.ClassCastException: com.sun.crypto.provider.RSACipher cannot be cast to javax.crypto.CipherSpi

在调用HTTPS相关代码时,需要通过@PowerMockIgnore({“javax.security.*”})忽略上述包,否则会出现以下异常:

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/security/auth/x500/X500Principal"

若不忽略“javax.script.*”对应的包,在执行单元测试时可能会出现以下日志:

ScriptEngineManager providers.next(): javax.script.ScriptEngineFactory: Provider jdk.nashorn.api.scripting.NashornScriptEngineFactory not a subtype

5. Mock框架依赖组件、版本差别等

5.1. 依赖组件的影响

5.1.1. javassist

当在项目中添加groupId为“javassist”的javassist依赖时,会导致单元测试程序无法正常启动。

例如添加对“javassist:javassist:3.12.1.GA”的依赖,在启动单元测试程序时会出现以下异常:

java.lang.NoSuchMethodError: javassist.CtClass.getDeclaredClasses()[Ljavassist/CtClass;

at org.powermock.core.transformers.javassist.ConstructorsMockTransformer.transform(ConstructorsMockTransformer.java:50)

查看项目依赖的组件,同时依赖了“org.javassist:javassist:3.x.x-GA”与“javassist:javassist:3.12.1.GA”,前者由“org.powermock:powermock-core:2.x.x”间接依赖。

javassist从“3.12.1.GA”之后的版本“3.13.0-GA”开始,groupId由“javassist”变为“org.javassist”。

去除项目中对“javassist:javassist”的依赖后,单元测试程序启动正常。

5.2. Mockito 2与Mockito 1版本的区别

在 https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#incompatible-changes-with-110 中,介绍了Mockito 2与Mockito 1.10版本不兼容的内容,其中常用的功能如下:

  • InvocationOnMock

在Mockito 1中,在Answer中使用的InvocationOnMock接口的实现类为InvocationImpl类,获取单个参数的方法为getArgumentAt();

在Mockito 2及以上版本中,InvocationOnMock接口的实现类为InterceptedInvocation类,获取单个参数的方法为getArgument()。

可参考示例TestCommonUtil类的getMockArg()方法,针对以上方法进行了兼容处理。

  • Mockito.any…()等方法是否支持null

对于Mockito.any…()及Mockito.any(SomeType.class)方法,在Mockito 1中支持null,在Mockito 2及以上版本中不支持null。

  • Mock/Spy对象的代理类名标志

Mock/Spy对象的代理类名标志,在Mockito 1中示例如下:

$$EnhancerByMockitoWithCGLIB$$96437470

在Mockito 2及以上版本中示例如下:

$MockitoMock$1151489917
  • Matchers/ArgumentMatcher类

在Mockito 1中,Mockito类继承自Matchers类;matches方法声明为“public abstract boolean matches(Object argument)”,在Matchers类中;isNotNull方法声明为“public static Object isNotNull()”;

在Mockito 2及以上版本中,Mockito类不继承自Matchers类,与Matchers类均继承自ArgumentMatchers类;matches方法声明为“public static String matches(Pattern pattern)”或“public static String matches(String regex)”,在ArgumentMatchers类中;isNotNull方法声明为“public static <T> T isNotNull()”。

5.3. PowerMock不同版本的区别

  • 需要添加的依赖组件不同

当使用Mockito 1版本时,需要添加的PowerMock依赖为“org.powermock:powermock-api-mockito”;当使用Mockito 2及以上版本时,需要添加的依赖为“org.powermock:powermock-api-mockito2”。

6. Mock与Stub

参考“Mocks Aren’t Stubs”( https://martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs )。Mock是用期望进行预编程的对象,这些对象构成了期望接收的调用的规范。Stub提供对测试过程中进行的调用的固定答复,通常不会对测试编程之外的内容进行响应。

参考“2. How about some stubbing?”( https://static.javadoc.io/org.mockito/mockito-core/latest/org/mockito/Mockito.html#stubbing )。Mock操作对应Mockito.mock()方法,Stub操作对应Mockito.when().thenReturn()/thenThrow()等方法。当方法被Stub后,将始终返回Stub的值,无论被调用多少次。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值