• JAVA单元测试用例——待续


    JAVA单元测试用例编写



      单元测试通常不依赖第三方组件(比如数据库),如果遇到了这些需要依赖第三方的情况,可以使用mock技术,模拟想要的情况,Java中mock的对象主要是Java方法和Java类。JUnit进行单元测试时可以结合使用Mockito和PowerMock来模拟外部依赖,增加测试可控。Mockito主要支持对实例的模拟(验证或直接跳过),PowerMock在Mockito的基础上扩展而来,支持Mockito的操作,是一个扩展了多个mock框架的、功能更加强大的框架。它使用一个自定义类加载器和字节码操作来模拟public方法(大多数情况)、static静态方法、构造方法、final类和方法、private私有方法、enum枚举类、去除静态初始化器等。

    一、常用注解(不区分)


    应用于测试类上


    • @RunWith(PowerMockRunner.class)
    • 指定JUnit 使用 PowerMock 框架中的单元测试运行器来做Object的Mock工作,在执行时,再交给delegate的runner去执行。
    • @PowerMockIgnore({…, …})
    • 默认情况下,PowerMock会用自己的类加载器去加载所有的类,除了system class(java.lang等目录下的类),使用PowerMockIgnore声明的类PowerMock也不会加载。
    • @PrepareForTest({…, …})
    • PowerMock提供注解,模拟final类、final方法或static静态方法时,需要添加该注解,并指定方法所在的类。如果需要指定多个类,在{}中添加多个类并用逗号隔开即可。

    应用于属性及方法上


    • @Before、@BeforeClass、@After、@AfterClass
    • @BeforeClass(@BeforeAll): 在测试类中只会被执行一次,注解在静态方法上。
    • @Before(@BeforeEach):每个测试方法均会执行一次,注解在非静态方法上。
    • 单元测试用例执行顺序: @BeforeClass -> @Before -> @Test -> @After -> @AfterClass。
    • 测试方法的调用顺序:@Before -> @Test -> @After。
    • @Mock
    • 创建一个Mock实例(类或接口),其所有属性均被置0或null,Mock函数默认不执行,有返回值的返回null、空集合或被对象类型包装的内置类型(Integer等)。
    • Mock对象会记住所有的交互,即对这个Mock对象做的操作都被记录下来,然后可以选择性验证(verify方法) 感兴趣的交互。
    // 类
    @Component
    public class TClass {
    
        public String getMessage(String str) {
            System.out.println(str);
            return str + "!!!";
        }
    }
    
    // 测试
    @Mock
    private TClass tClass;
    
    @Test
    public void testTClass() {
    	// 不会真正地执行
        tClass.getMessage("ho");
        // 验证正确,因为该行为被记录
        Mockito.verify(tClass).getMessage("ho");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • @Spy
    • 创建一个没有Mock的实例,所有成员方法默认按照原方法的逻辑执行,直到被Mock返回某个具体的值为止。
    • 被@Spy注解标注的变量需要初始化,否则执行时会抛出异常。
    • 如果发现@Spy注解修饰的变量是 null,会自动调用类的无参构造函数(必须有)来初始化,因此 new XXX() 可写可不写。
    • 对Spy变量打桩时,使用 when 去设置模拟值,真实方法逻辑依然会执行,但模拟了返回结果。
    • @InjectMocks
    • 创建一个可以调用真实方法逻辑的实例,并注入所有用@Mock或@Spy注解创建的实例。最终测试的方法所在类的实例通过该注解标注并创建。
    • @Captor
    • 字段级别创建参数捕获器,在测试方法启动前(在@Before修饰的方法中),必须调用MockitoAnnotations.openMocks(this)进行初始化。

    二、常用方法

    2.1 Mockito

    org.mockito.Mockito类中提供了大量的静态方法,其中较多关键方法封装在静态类MockitoCore中。

    2.1.1 mock和spy

    • Mockito.mock()
    public static <T> T mock(Class<T> classToMock) {
            return mock(classToMock, withSettings());
    }
    // 最终在 MockitoCore 中实现
    public static <T> T mock(Class<T> classToMock, MockSettings mockSettings) {
            return MOCKITO_CORE.mock(classToMock, mockSettings);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 通过给定类或接口构建对应的mock实例。
    • 对于非final类(接口、普通类等)的非final方法,不必使用@RunWith和@PrepareForTest注解,反之则相反。
    • spy()
    // 模拟类
    public static <T> T spy(Class<T> classToSpy) {
            return MOCKITO_CORE.mock(
                    classToSpy, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS));
    }
    
    // 模拟方法
    public static <T> T spy(T object) {
            return MOCKITO_CORE.mock(
                    (Class<T>) object.getClass(),
                    withSettings().spiedInstance(object).defaultAnswer(CALLS_REAL_METHODS));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 可以只模拟对象的部分方法,同时其它方法跟原来一样,callRealMethod()方法也能实现部分mock。
    • 通过模拟类的方式可直接调用静态方法
    • 通过when语句设置过的方法调用实际方法但返回模拟结果,没有通过when语句设置的方法调用原有方法并返回实际结果。
    • 通常结合doXXX() 方法,只有使用doReturn()方法时不会调用实际方法。
    • spy对象可以视作被spy的对象的浅拷贝

    2.1.2 模拟类方法调用

    • mockStatic
    @Incubating
    @CheckReturnValue
    public static <T> MockedStatic<T> mockStatic(Class<T> classToMock) {
            return mockStatic(classToMock, withSettings());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 结合注解@PrepareForTest使用,传入注解中的类进行mock后可以直接在测试方法中模拟该类的静态方法。
    • 对于有返回值的类方法,直接调用thenReturn()模拟结果值,对于没有返回值的类方法,使用doNothing()方法解决。
    // 模拟没有返回值的类方法
    doNothing().when(TestClass.class);
    TestClass.testMethod(any(), ...);
    
    • 1
    • 2
    • 3

    2.1.3 模拟调用方法

    • when()
    // 核心在 MockitoCore 中实现
    public static <T> OngoingStubbing<T> when(T methodCall) {
            return MOCKITO_CORE.when(methodCall);
    }
    
    • 1
    • 2
    • 3
    • 4

    2.1.4 打桩

    • doReturn()
    • 当作用于一个spy对象时,不同于thenReturn()方法,该方法不会调用真实的逻辑,可以规避逻辑中复杂的外部依赖问题。
    • 覆盖之前模拟的异常。
    • doNothing()
    • 主要用于void方法。
    • 对void方法链式打桩,doNothing().doThrow(new TestException()).when(testClass).testVoidMethod(),第一次调用方法时doNothing,第二次抛异常。
    • doThrow()
    • 模拟抛出的异常,可以传入异常类或具体的实例。
    • 对于传入异常类的情况,每次调用都生成新的异常实例。
    • 可以传入多个异常类进行连续打桩,对每次调用抛出不同的异常类的实例。
    • doCallRealMethod()
    • 对于一个mock对象,可以通过该方法调用其测试方法的具体逻辑。
    • 更推荐使用spy。

    2.1.5 验证方法

    • verify()
    • 验证方法是否被调用,默认验证一次,传递VerificationMode类型参数作为验证模式。
    // 默认验证是否调用了一次
    public static <T> T verify(T mock) {
    		// 调用方法 2
            return MOCKITO_CORE.verify(mock, times(1));
    }
    
    // 2
    public static <T> T verify(T mock, VerificationMode mode) {
            return MOCKITO_CORE.verify(mock, mode);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • times(int)
    • 设置验证方法调用了几次,返回VerificationMode。
    public static VerificationMode times(int wantedNumberOfInvocations) {
    		// 静态工厂
            return VerificationModeFactory.times(wantedNumberOfInvocations);
    }
    
    • 1
    • 2
    • 3
    • 4

    2.2 ArgumentMatchers参数匹配

    • any()
    • 用于模拟打桩方法的入参,可以匹配任何内容,包括null和varargs。

    2.3 OngoingStubbing打桩

    • thenReturn()
    • 模拟返回值,用在when()方法之后。
    • thenThrow()
    • 模拟异常,多用于带返回值的方法,对于void方法,一般用doThrow()方法。

    2.4 Assert类

    • assertThrows()
    • 允许使用异常类的超类型
    public static <T extends Throwable> T assertThrows(Class<T> expectedThrowable, 
    													ThrowingRunnable runnable) {}
    
    • 1
    • 2
    • assertEquals()
    • assertTrue()

    参考文档

    《Mockito中文文档》

    《Mockito官网》

    《Mockito英文文档》

  • 相关阅读:
    揭秘弹幕游戏制作
    SpringBoot的自动配置原理
    【网页设计】期末大作业:化妆品主题——绿色大气的html5响应式化妆品护肤品肌肤网页设计(11页)...
    REF615 REU615 RED615 人工智能在工业中的第一步
    maven本地化jbarcode-0.2.7.jar
    Android 10.0 framework层设置后台运行app进程最大数功能实现
    分享禁止Win10更新的两种方法
    简述一下伪共享的概念以及如何避免
    浅谈C#字符串构建利器StringBuilder
    python语义分割标签的游程长度编码和解码
  • 原文地址:https://blog.csdn.net/weixin_43415481/article/details/125858679