Java单元测试实践-10.Mock非静态方法

news/2024/7/7 15:39:32

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类。


http://www.niftyadmin.cn/n/4057565.html

相关文章

Java单元测试实践-11.Mock后Stub Spring的@Component组件

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Spring Context加载次数 在使用PowerMock时&#xff0c;每执行一个测试类&#xff0c;Spring Context都会重新加载一次&#xff0…

Java单元测试实践-12.Answer与未Stub的Spring组件方法

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Answer与未Stub的Spring组件方法 1.1. 未Stub的方法的返回值 对于Spring的Component组件的Mock对象未Stub的方法&#xff0c;返回…

一本书,一个人

本文转自 http://smartcard.blog.com/tag/%E7%8E%8B%E7%88%B1%E8%8B%B1/ 国内的智能卡发展&#xff0c;可以上溯到上个世纪90年代初期。当时大家对于磁卡都知之甚少&#xff0c;更不要说IC卡、智能卡了。由于那段时间国际通用的智能卡标准还没有怎么得到很好地贯彻执行&#xf…

不受环境干扰,这套声学全息方案实现了虚实交互

一谈到全息显示&#xff0c;大家默认想到的就是光学全息方案&#xff0c;比如需要将光投射到某样东西上&#xff0c;比如视网膜&#xff0c;或是烟雾等介质上&#xff0c;才能成像。市面上一些常见的2D、3D全息方案&#xff0c;如全息风扇、Voxon全息系统等等&#xff0c;分别采…

Java单元测试实践-13.Spy后Stub Spring的@Component组件

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Spy后Stub Spring的Component组件 Spring的Component组件的Spy对象&#xff0c;未Stub的方法会执行真实方法。在使用Spy对象时&am…

Java单元测试实践-15.Stub、Replace、Suppress Spring的方法

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Stub、Replace、Suppress Spring的方法 对Spring的Component组件的方法进行Stub、Replace、Suppress时&#xff0c;与对Mock/Spy对…

俺是一个三俗的程序员

俺很三俗&#xff0c;想起来一定尽量回复过来。 想起来一个&#xff0c;就是在水园天天有人发大道理&#xff0c;俺每次看见心里骂&#xff0c;废话一堆。 看见有人忽悠新技术&#xff0c;心里骂&#xff0c;不就是技术垄断吗&#xff1f; 收到垃圾邮件&#xff0c;让信X的&…