MockiTo 测试用mock框架

引入

编写UT测试用例时,使用mock可以只针对待测方法进行验证,而无需考虑真实数据和调用方法的影响。

依赖

<dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.10.19</version>
            <scope>test</scope>
</dependency>

使用

参考文章:https://www.cnblogs.com/yinyunmoyi/p/14203794.html

初始化

测试类中进行UT编写之前需要初始化MockiTo,自动创建mock对象

    @BeforeClass
    public void initMocks() throws Exception{
        System.out.println("Before Class");
        //MockitoAnnotations.initMocks(this),其中this就是单元测试所在的类,在initMocks函数中Mockito会根据类中不同的注解(如@Mock, @Spy等)创建不同的mock对象,即初始化工作
        MockitoAnnotations.initMocks(this);
    }

@Before, @BeforeClass, @BeforeEach 和 @BeforeAll 之间的不同

特性

Junit 4 Junit 5
  • 在当前类的所有测试方法之前执行。
  • 注解在静态方法上。
  • 此方法可以包含一些初始化代码。
@BeforeClass @BeforeAll
  • 在当前类中的所有测试方法之后执行。
  • 注解在静态方法上。
  • 此方法可以包含一些清理代码。
@AfterClass @AfterAll
  • 在每个测试方法之前执行。
  • 注解在非静态方法上。
  • 可以重新初始化测试方法所需要使用的类的某些属性。
@Before @BeforeEach
  • 在每个测试方法之后执行。
  • 注解在非静态方法上。
  • 可以回滚测试方法引起的数据库修改。
@After

@AfterEach

 

 

 

 

 

 

 

 

 

 

 

标记的代码@Before在每次测试之前执行,而@BeforeClass在整个测试夹具之前运行一次。如果你的测试类有十个测试,则@Before代码将执行十次,但@BeforeClass仅执行一次。

 

通常,@BeforeClass当多个测试需要共享相同的计算昂贵的设置代码时,可以使用。建立数据库连接属于此类。你可以将代码从@BeforeClass移到@Before,但是测试运行可能需要更长的时间。注意,标记的代码@BeforeClass作为静态初始化程序运行,因此它将在创建测试夹具的类实例之前运行。

 

-https://www.cnblogs.com/yaoyaoo/p/14344684.html

坑:

1.@BeforeClass 可能导致没法初始化,建议使用@BeforeEach

2.不加@RunWith可能没法初始化Mock对象

3.initMocks 过时,用openMocks

    @BeforeEach
    public void init() {
        System.out.println("init");
        MockitoAnnotations.openMocks(this);
    }
@RunWith(MockitoJUnitRunner.class)// import org.mockito.junit.MockitoJUnitRunner;

 

创建mock

一般使用注解方式

@InjectMocks

用它标注的类会自动装配其中已经被在当前测试类中用 @Mock 和 @Spy 标注的字段(按名称匹配),该类.method(param...)是真实方法调用    =》该注解标记在待测类上

@Mock

创建要mock的对象,如果某个对象被 mock,那么它将不再拥有原本的功能,全部的方法都被替换掉了,只会返回设置好的值,如果 mock 对象的某个方法没有设置,那么就会返回 null =》该注解标注在要mock的类上

@InjectMocks
ModelDaoImpl modelDao; 

@Mock
Mediator mediator;

...{
    // 这里调用的create是真实的方法,这里面如果有mediator调用某个方法,就可以通过自动装配事先设置它的行为了
    Mockito.when(mediator.get(0)).thenReturn("foo");
    modelDao.create();
    ...
}

 

打桩(stup)

设置一类行为

有返回值

方式一:会进入方法内,只是返回值是设定的返回值

Mockito.when(A.method(param...)).thenReturn(B);

方式二:并不会进入方法内,触发该方法就直接返回设定的值

Mockito.doReturn(C).when(A).method(B);

无返回值

返回值类型为 void的方法才能使用

doNothing().when(A.method(param...));

 

部分模拟

对于局部变量,有时候是不好准确模拟的,即使创建一个相同的对象

如这种情况

methodA() {
Request request = new Request();

// 设置request的属性
...

// 调用方法
Response response = serviceInvoker.invoke(SERVICE_NAME, request);
}

即便模拟invoke

// 创建request对象并设置其属性
Mockito.when(serviceInvoker.invoke(SERVICE_NAME, request)).thenReturn("foo");

也是行不通的,因为在待测试的代码中,request 是一个局部变量,它的地址和我们创建出来的对象地址是不同的,也就无法准确的进行模拟

解决方式可以是将生成Request的代码抽出来为一个方法,将该方法局部模拟,使用spy,spy部分模拟的对象,除了设定的方法,其他的都会按照原有真实方法执行

spy:

  一,注解 @spy

  二,手动

// 使用部分模拟需要用spy方法,它必须是某个已经建立好的对象的封装,不能用class对象为构造参数
List list = new LinkedList();
List mockedList = Mockito.spy(list);

将生成Request的代码抽出来为一个方法

Request request = getRequest();

// 调用方法
Response response = serviceInvoker.invoke(SERVICE_NAME, request);

来mock,instance是spy出来的

// 模拟getRequest方法的行为
Mockito.when(instance.getRequest()).thenReturn(request);

// 完成我们一开始想要的行为定义
Mockito.when(serviceInvoker.invoke(SERVICE_NAME, request)).thenReturn("foo");

或者不在意参数,直接用any()

 

mock多次调用返回不同结果

        // 第一次返回1,第二次返回2
        Mockito.doReturn(1).doReturn(2).when(b).method();

 

mock静态和私有方法

这是个坑,try出来就无效了,只适合单独测试静态方法

 

 

 

 

 

验证(verify)

 

posted on 2022-04-12 22:01  or追梦者  阅读(715)  评论(0编辑  收藏  举报