概述
Java单元测试实践-00.目录(9万多字文档+700多测试示例)
https://blog.csdn.net/a82514921/article/details/107969340
1. Mock非静态方法
对非静态方法的进行Mock等处理时,部分情况与对Spring的@Component组件的处理类似,主要在后续Spring相关的内容进行详细说明,本部分的内容相对简略。
1.1. Mock后Stub非静态方法
1.1.1. 生成非静态方法对应的类的Mock对象
参考 https://github.com/mockito/mockito/wiki 。使用mock()方法可以通过代码创建Mock对象。
参考 https://static.javadoc.io/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mock-java.lang.Class- 。使用Mockito.mock()方法可以创建指定的类或接口的Mock对象。
对非静态方法进行Stub时,可以使用Mockito.mock()方法创建Mock对象,再对Mock对象的方法进行Stub。
1.1.2. Stub单例模式类的非静态方法
对于单例模式类的非静态方法进行Stub,可以按照以下步骤执行:
-
对于使用单例模式的类,使用@PrepareForTest注解及PowerMockito.mockStatic()方法进行处理;
-
通过Mockito.mock()方法创建对应类的Mock对象;
-
对获取实例的静态方法进行Stub,将返回修改为Mock对象;
-
针对需要Stub的非静态方法,对Mock对象进行Stub。
示例如下,可参考示例TestNStMSingleton1类。
PowerMockito.mockStatic(TestNonStaticSingleton1.class);
TestNonStaticSingleton1 testNonStaticSingleton1Mock = Mockito.mock(TestNonStaticSingleton1.class);
Mockito.when(TestNonStaticSingleton1.getInstance()).thenReturn(testNonStaticSingleton1Mock);
Mockito.when(testNonStaticSingleton1Mock.test1()).thenReturn(TestConstants.MOCKED);
1.1.3. Stub无参数构造函数
使用“PowerMockito.whenNew(需要Stub的构造函数所在的类.class).withNoArguments().thenReturn();”,可以对无参数构造函数进行Stub,thenReturn()方法的参数指定构造函数被Stub后返回的对象,可以使指定类的无参数构造函数返回指定的对象,可为被Mock的对象,如下所示:
TestNonStaticNoArg1 testNonStaticNoArg1Mock = Mockito.mock(TestNonStaticNoArg1.class);
Mockito.when(testNonStaticNoArg1Mock.test1()).thenReturn(TestConstants.MOCKED);
PowerMockito.whenNew(TestNonStaticNoArg1.class).withNoArguments().thenReturn(testNonStaticNoArg1Mock);
1.1.3.1. 在测试代码调用构造函数
在测试代码执行被Stub的构造函数时,可以不使用@PrepareForTest注解,Stub也能够生效,生成对象为Stub指定的对象。
例如执行上述代码对TestNonStaticNoArg1类的无参数构造函数进行Stub,当执行new TestNonStaticNoArg1()生成TestNonStaticNoArg1类的实例时,生成的实例为上述指定的Mock对象,test1()方法的返回值为TestConstants.MOCKED。
可参考示例TestNStMConstructorNoArgNoPrepare类test1方法。
1.1.3.2. 在被测试代码调用构造函数
通常情况下,被Stub的构造函数是在被测试代码中执行的,而不是在测试代码中执行的。因此主要需要考虑在被测试代码调用被Stub构造函数的情况。
在被测试代码执行被Stub的构造函数,在测试代码中不使用@PrepareForTest注解时,Stub不生效,生成对象为原始对象。可参考示例TestNStMConstructorNoArgNoPrepare类test2方法。
在被测试代码执行被Stub的构造函数,需要在测试代码的类级别或方法级别,使用@PrepareForTest注解指定调用构造函数的类,Stub才能生效,生成对象为Stub指定的对象。
示例如下,在被测试代码TestService1Impl类的genNoArg1()方法中调用了TestNonStaticNoArg1类的构造函数,因此需要在测试代码使用@PrepareForTest注解指定TestService1Impl类。可参考示例TestNStMConstructorNoArgPrepareClass、TestNStMConstructorNoArgPrepareMethod类。
// 测试代码
@PrepareForTest({TestService1Impl.class})
// 被测试代码
public class TestService1Impl implements TestService1 {
@Override
public TestNonStaticNoArg1 genNoArg1() {
return new TestNonStaticNoArg1();
}
}
1.1.3.3. 在被测试代码的内部类调用构造函数
被Stub的构造函数有可能在被测试代码的内部类中执行,例如在被测试代码中创建线程,在线程中调用构造函数;或在被测试代码中的命名内部类中调用构造函数,可参考被测试代码TestNonStatic1类test1、test2方法。
在被测试代码的内部类调用构造函数,在测试代码中不使用@PrepareForTest注解时,对构造函数的Stub不生效,生成对象为原始对象,可参考示例TestNStMConstructorNoArgNoPrepare2类。
在被测试代码的内部类调用构造函数,在测试代码中使用@PrepareForTest注解的value参数指定调用构造函数的内部类所在的类时,对构造函数的Stub不生效,生成对象为原始对象,可参考示例TestNStMConstructorNoArgNoPrepare2类。
在被测试代码的内部类调用构造函数,需要在测试代码中使用@PrepareForTest注解的fullyQualifiedNames参数指定调用构造函数的类所在的包,对构造函数的Stub才能生效,生成对象为Stub指定的对象。
示例如下,在被测试代码TestNonStatic1类test1()方法中创建线程执行TestNonStaticNoArg1类的构造函数,因此需要在测试代码使用@PrepareForTest注解的fullyQualifiedNames参数指定TestNonStatic1类所在的包。
// 测试代码
@PrepareForTest(fullyQualifiedNames = "com.adrninistrator.non_static.*")
// 被测试代码
public class TestNonStatic1 {
private TestNonStaticNoArg1 testNonStaticNoArg1 = null;
public TestNonStaticNoArg1 test1() {
Thread thread = new Thread(() -> testNonStaticNoArg1 = new TestNonStaticNoArg1());
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
logger.error("error: ", e);
Thread.currentThread().interrupt();
}
return testNonStaticNoArg1;
}
}
示例如下,在被测试代码TestNonStatic1类的内部类TestNonStatic1Inner的testNew()方法中调用了TestNonStaticNoArg1类的构造函数,在TestNonStatic1类的test2()方法中调用了内部类TestNonStatic1Inner的testNew()方法,因此需要在测试代码使用@PrepareForTest注解的fullyQualifiedNames参数指定TestNonStatic1类所在的包。
// 测试代码
@PrepareForTest(fullyQualifiedNames = "com.adrninistrator.non_static.*")
// 被测试代码
public class TestNonStatic1 {
public TestNonStaticNoArg1 test2() {
return new TestNonStatic1Inner().testNew();
}
private class TestNonStatic1Inner {
private TestNonStaticNoArg1 testNew() {
return new TestNonStaticNoArg1();
}
}
}
可参考示例TestNStMConstructorNoArgPrepareName类。
关于@PrepareForTest注解的fullyQualifiedNames参数说明可参考前文。
1.1.4. Stub有参数构造函数
1.1.4.1. 有参数构造函数的Stub条件
对有参数构造函数进行Stub时,Stub条件可以使用PowerMockito.whenNew().withAnyArguments()、或PowerMockito.whenNew().withArguments()等方法。
- Stub条件为任意参数
在对有参数构造函数进行Stub时,使用PowerMockito.whenNew().withAnyArguments()方法,可将对构造函数的Stub条件设置为,调用构造函数时使用任意参数均进行Stub。示例如下,可参考示例TestNStMConstructorWithArgNPAny类test1、test2方法,TestNStMConstructorWithArgWPAny类。
TestNonStaticWithArg1 testNonStaticWithArg1Mock = Mockito.mock(TestNonStaticWithArg1.class);
Mockito.when(testNonStaticWithArg1Mock.test1()).thenReturn(TestConstants.MOCKED);
PowerMockito.whenNew(TestNonStaticWithArg1.class).withAnyArguments().thenReturn
(testNonStaticWithArg1Mock);
- Stub条件为指定数量及指定类型的参数
在对有参数构造函数进行Stub时,使用PowerMockito.whenNew().withArguments()方法,并将withArguments()方法的参数设置为Mockito.any(参数类型.class),可将Stub条件设置为在调用构造函数时,若使用的参数数量类型满足要求则进行Stub。示例如下,可参考示例TestNStMConstructorWithArgNPAnyType类test1方法,TestNStMConstructorWithArgWPAnyType类test1方法。
TestNonStaticWithArg1 testNonStaticWithArg1Mock = Mockito.mock(TestNonStaticWithArg1.class);
Mockito.when(testNonStaticWithArg1Mock.test1()).thenReturn(TestConstants.MOCKED);
PowerMockito.whenNew(TestNonStaticWithArg1.class).withArguments(Mockito.anyString()).thenReturn(testNonStaticWithArg1Mock);
- Stub条件为指定值的参数
在对有参数构造函数进行Stub时,使用PowerMockito.whenNew().withArguments()方法,并将withArguments()方法的参数设置为指定值,可将Stub条件设置为在调用构造函数时,若使用的参数等于指定值则进行Stub。示例如下,可参考示例TestNStMConstructorWithArgNPSpecified类test1方法,TestNStMConstructorWithArgWPSpecified类test1方法。
TestNonStaticWithArg1 testNonStaticWithArg1Mock = Mockito.mock(TestNonStaticWithArg1.class);
Mockito.when(testNonStaticWithArg1Mock.test1()).thenReturn(TestConstants.MOCKED);
PowerMockito.whenNew(TestNonStaticWithArg1.class).withArguments(TestConstants.FLAG1).thenReturn
(testNonStaticWithArg1Mock);
withArguments()方法可以使用ArgumentMatchers接口的其他方法作为有参数构造函数的Stub条件,略。
1.1.4.2. 使用@PrepareForTest注解
Stub有参数构造函数时,对@PrepareForTest注解的使用情况与无参数构造函数类似。
- 在测试代码调用构造函数,可以不使用@PrepareForTest注解,Stub也能够生效。
对于Stub条件为任意参数,可参考示例TestNStMConstructorWithArgNPAny类test1、test2方法,
对于Stub条件为指定数量及指定类型的参数,可参考示例TestNStMConstructorWithArgNPAnyType类test1方法,
对于Stub条件为指定值的参数,可参考示例TestNStMConstructorWithArgNPSpecified类test1方法;
- 在被测试代码执行被Stub的构造函数,需要在测试代码的类级别或方法级别使用@PrepareForTest注解指定被测试代码对应的类,Stub才能生效。
对于Stub条件为任意参数,可参考示例TestNStMConstructorWithArgNPAny类test3方法,TestNStMConstructorWithArgWPAny类;
对于Stub条件为指定数量及指定类型的参数,可参考示例TestNStMConstructorWithArgNPAnyType类test3方法,TestNStMConstructorWithArgWPAnyType类test1方法;
对于Stub条件为指定值的参数,可参考示例TestNStMConstructorWithArgWPSpecified类test1方法。
- 在被测试代码的内部类调用构造函数,需要在测试代码中使用@PrepareForTest注解的fullyQualifiedNames参数指定构造函数所在的类所在的包,Stub才能生效,示例略。
1.1.4.3. 不满足有参数构造函数Stub条件
当使用PowerMockito.whenNew().withArguments()方法对有参数构造函数进行Stub时,当调用构造函数时的参数不满足Stub条件时,生成的对象为null。
对于Stub条件为指定数量及指定类型的参数,可参考示例TestNStMConstructorWithArgNPAnyType类test2方法,TestNStMConstructorWithArgWPAnyType类test2方法;
对于Stub条件为指定值的参数,可参考示例TestNStMConstructorWithArgNPSpecified类test2方法,TestNStMConstructorWithArgWPSpecified类test2方法。
1.1.5. 不支持对原始对象进行Stub
Stub操作需要对Mock或Spy对象执行,不支持对原始对象执行。当对原始对象进行Stub时,会出现异常。
例如对TestNonStatic1类的原始对象进行Stub,会出现以下异常:
TestNonStatic1 testNonStatic1 = new TestNonStatic1();
Mockito.when(testNonStatic1.test1()).thenReturn(null);
org.mockito.exceptions.misusing.MissingMethodInvocationException
when() requires an argument which has to be 'a method call on a mock'.
可参考示例TestNStMockRaw类。
1.2. Spy后Stub非静态方法
1.2.1. 生成非静态方法对应的类的Spy对象
参考“13. Spying on real objects”( https://static.javadoc.io/org.mockito/mockito-core/latest/org/mockito/Mockito.html#spy )。
Mockito类的spy()方法可以创建真实对象的Spy对象。Spy对象未被Stub的方法会执行真实方法。 Mockito不会将调用委托给传入的真实实例,而是创建真实实例的副本。当调用Spy对象的未Stub方法而不是调用真实实例的对应方法时,不会对真实实例产生影响。
1.2.2. Mockito.spy()方法比较
Mockito支持<T> T spy(T object)与<T> T spy(Class<T> classToSpy)方法,参数分别需要传入对象实例,或类的Class对象。Mockito类的spy()方法不支持传入Answer、MockSettings等参数。
- 对实例执行spy
Mockito.spy()方法支持对某个类的实例执行,示例如下,可参考示例TestNStSpyCompare类test1方法。
TestNonStaticNoArg1 testNonStaticNoArg1Spy = Mockito.spy(new TestNonStaticNoArg1());
- 对包含无参数构造函数类的Class对象执行Spy
Mockito.spy()方法支持对包含无参数构造函数类的Class对象执行, 会执行无参数构造函数 ,示例如下,可参考示例TestNStSpyCompare类test2方法。
TestNonStaticNoArg1 testNonStaticNoArg1Spy = Mockito.spy(TestNonStaticNoArg1.class);
- 对不包含无参数构造函数类的Class对象执行Spy
Mockito.spy()方法不支持对不包含无参数构造函数类的Class对象执行,会出现异常。示例如下,可参考示例TestNStSpyCompare类test3方法。
Mockito.spy(TestNonStaticWithArg1.class);
异常信息示例如下:
org.mockito.exceptions.base.MockitoException
Unable to create mock instance of type 'TestNonStaticWithArg1'
org.mockito.creation.instance.InstantiationException
Unable to create instance of 'TestNonStaticWithArg1$MockitoMock$1139741189'.
Please ensure that the target class has a 0-arg constructor.
1.3. 对非静态方法进行Suppress
使用PowerMockito.suppress()方法支持对非静态方法进行Suppress。
1.3.1. Suppress构造函数
使用PowerMockito类的void suppress(Constructor<?> constructor)方法,可以对单个构造函数进行Suppress,使用PowerMockito类的void suppress(Constructor<?>[] constructors)方法,可以对构造函数数组进行Suppress。
被Suppress的构造函数会被禁止,调用被Suppress的构造函数时什么也不会做。
禁止构造函数不需要使用@PrepareForTest注解。
1.3.1.1. Suppress唯一的构造函数
使用PowerMockito.constructor()方法可以获取指定类的唯一的构造函数,在使用时需要指定类的Class对象,返回类型为Constructor<T>,当指定类存在多个构造函数时,会抛出TooManyConstructorsFoundException异常。
使用PowerMockito.suppress()方法Suppress类的唯一构造函数示例如下:
PowerMockito.suppress(PowerMockito.constructor(TestNonStatic3.class));
当类只包含一个构造函数时,Suppress成功,可参考示例TestNStSuppressConstructor1类test1方法。
当类只包含多个构造函数时,PowerMockito.constructor()方法会出现异常,异常信息示例如下。可参考示例TestNStSuppressConstructor1类test2方法。
org.powermock.reflect.exceptions.TooManyConstructorsFoundException
Several matching constructors found, please specify the argument parameter types so that PowerMock can determine which method you're referring to.
Matching constructors in class com.adrninistrator.non_static.TestNonStatic4 were:
com.adrninistrator.non_static.TestNonStatic4( )
com.adrninistrator.non_static.TestNonStatic4( java.lang.String.class )
1.3.1.2. Suppress全部的构造函数
使用PowerMockito.constructorsDeclaredIn()方法可以获取指定类的全部构造函数,返回类型为Constructor<?>[],在使用时需要指定类的Class对象。
使用PowerMockito.suppress()方法Suppress类的全部构造函数示例如下,可参考示例TestNStSuppressConstructorsDeclaredIn1类。
PowerMockito.suppress(PowerMockito.constructorsDeclaredIn(TestNonStatic3.class));
1.3.1.3. Suppress默认的构造函数
使用PowerMockito.defaultConstructorIn()方法可以获取指定类的默认构造函数,返回类型为Constructor<?>[],当指定的类不存在默认构造函数时,会抛出ConstructorNotFoundException异常,在使用时需要指定类的Class对象。
使用PowerMockito.suppress()方法Suppress类的默认构造函数示例如下:
PowerMockito.suppress(PowerMockito.defaultConstructorIn(TestNonStatic3.class));
- 仅存在一个无参数构造函数类
对于仅存在一个无参数构造函数的类,PowerMockito.defaultConstructorIn()方法可获取到无参数构造函数,对其Suppress成功。可参考示例TestNStSuppressDefaultConstructorIn1类test1方法。
- 存在多个构造函数的类
对于存在多个构造函数的类,PowerMockito.defaultConstructorIn()方法可获取到无参数构造函数,对其Suppress成功,对于有参数构造函数不会Suppress。可参考示例TestNStSuppressDefaultConstructorIn1类test2方法。
- 无构造函数的类
对于无构造函数的类,会自动添加一个无参数构造函数,PowerMockito.defaultConstructorIn()方法可获取到无参数构造函数,对其Suppress成功。可参考示例TestNStSuppressDefaultConstructorIn1类test3方法。
- 存在有参数构造函数,但不存在无参数构造函数的类
对于存在有参数构造函数,但不存在无参数构造函数的类,不会自动添加一个无参数构造函数,PowerMockito.defaultConstructorIn()方法无法获取到无参数构造函数,会出现异常,异常信息如下所示。可参考示例TestNStSuppressDefaultConstructorIn1类test3方法。
org.powermock.reflect.exceptions.ConstructorNotFoundException
Couldn't find a default constructor in com.adrninistrator.non_static.TestNonStaticWithArg2.
1.3.1.4. 使用SuppressCode.suppressConstructor()进行Suppress
PowerMockito.suppress()方法可以使用SuppressCode.suppressConstructor()方法替代,可参考示例TestNStSuppressConstructor2、TestNStSuppressConstructorsDeclaredIn2、TestNStSuppressDefaultConstructorIn2类。
最后
以上就是风趣墨镜为你收集整理的Java单元测试实践-10.Mock非静态方法1. Mock非静态方法的全部内容,希望文章能够帮你解决Java单元测试实践-10.Mock非静态方法1. Mock非静态方法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复