首页 > 其他分享 >Mockito用法总结

Mockito用法总结

时间:2024-09-04 12:23:14浏览次数:7  
标签:总结 Goods Mockito goodsMock println 用法 class out

Mockito的是用来做什么的

Mockito主要用于单元测试过程中模拟被调用方法的

依赖

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.8.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>4.8.0</version>
    <scope>test</scope>
</dependency>

版本说明,3.4之前Mockito不能模拟静态方法,所以一般和powermock一起用3.4以后已经不需要powermock,
下面是 powermock的最新版本已 2020年11月1日的版本,所以建议直接使用mockito 3.4以后的版本

<!-- Power Mock -->
<dependency>
  <groupId>org.powermock</groupId>
  <artifactId>powermock-module-junit4</artifactId>
  <version>2.0.9</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.powermock</groupId>
  <artifactId>powermock-api-mockito2</artifactId>
  <version>2.0.9</version>
  <scope>test</scope>
</dependency>

创建Mock对象

可以使用 @Mock,@Spy,@InjectMocks 创建模拟对象

@RunWith(MockitoJUnitRunner.class)
public class MockItoTest {
    
    
    @Mock
    GoodsExMapper goodsExMapper;

    @Mock
    SqlSessionFactory sqlSessionFactory;

    @InjectMocks
    GoodsServiceImpl goodsServiceImpl;

    @Mock
    GoodsServiceImpl goodsServiceImplMock;

    @Spy
    private GoodsServiceImpl goodsServiceImplSpy;
    
    
    //省略

}

也可以代码里面创建,如果没有在类上写上@RunWith(MockitoJUnitRunner.class,可以使用 MockitoAnnotations.openMocks(this)来让当前teset类里面的@Mock @Spy 等注解生效

/**
     * 初始化Mock对象
     */
    @Test
    public void initMock() {
        //如果没有在类上写上@RunWith(MockitoJUnitRunner.class,可以使用 MockitoAnnotations.openMocks(this)来让当前teset类里面的@Mock @Spy 等注解生效
        System.out.println( goodsExMapper );
        MockitoAnnotations.openMocks(this);
        System.out.println( goodsExMapper );


        //除了使用@Mock @Spy 以外还能使用对应的方法常见mock对象
        Goods goodsMock = Mockito.mock(Goods.class);
        Goods goodsSpy = Mockito.spy(Goods.class);
    }

@Mock @InjectMocks @Spy 的区别

  • @Mock的对象所有方法都不会真实调用,会根据返回值类型的默认值返回,可以通过Mockito.when插桩
  • @InjectMocks 总是会调用真的实现方法,可以通过Mockito.when插桩,插桩值影响返回值,真实的方法依旧会被调用
  • @Spy 默认调用真实的实现方法,可以通过Mockito.return插桩,插桩后不会调用真实的方法
@Test
public void Mock() {
    //@Mock 的对象方法不会被真实调用,放回默认值
    System.out.println("goodsServiceImplMock.add(Goods.randomGoods() ) = " + goodsServiceImplMock.add(Goods.randomGoods()));
}
@Test
public void injectMocks() {
    //@InjectMocks的对象 方法真实的调用,并且活自动注入mock类(@mock,@spy 标注的成员变量的自动注入)
    System.out.println("goodsServiceImpl.add(Goods.randomGoods() ) = " + goodsServiceImpl.add(Goods.randomGoods()));
}

@Test
public void spy() {
    //@Spy 的对象方法被真是的调用,但是里面的成员变量不会自动加载,,使用里面的成员变量会报空指针
    System.out.println("goodsServiceImplSpy.add(Goods.randomGoods() ) = " + goodsServiceImplSpy.add(Goods.randomGoods()));
}

模拟普通方法

  • Mockito.when 的时候被mock方法会调用一次,并且对@InjectMock能用
  • Mockito.doReturn 方法不会被调用,并且对@InjectMock不能用
  • 被模拟的的时候 @Mock,@Spy 不会调用原来的方法,@InjectMocks总会调用真实的方法,然后修改返回值
@Test
public void mockMethod() {

    //@mock Mockito.when 有效
    //@mock Mockito.doReturn 有效
    //GoodsServiceImpl impl = goodsServiceImplMock;

    //@Spy Mockito.when 无效
    //@Spy Mockito.doReturn 有效
   // GoodsServiceImpl impl = goodsServiceImplSpy;

    //@InJect Mockito.when 有效
    //@InJect Mockito.doReturn 无效
    GoodsServiceImpl impl = goodsServiceImpl;


    ReflectionTestUtils.setField(impl,"goodsExMapper",goodsExMapper);
    System.out.println("goodsServiceImplSpy.add(new Goods())1 = " + impl.add(new Goods()));


    //Mockito.when( impl.add( Mockito.any(Goods.class) ) ).thenReturn( 99 );
    Mockito.doReturn(88).when( impl ).add( Mockito.any( Goods.class ));
    //给 add方法指定插桩,固定返回99

    //这里的方法插桩以后不会真实的调用
    System.out.println("goodsServiceImplSpy.add(new Goods())2 = " + impl.add(new Goods()));

}

final方法的模拟

@Test
public void mockFinalMethod() {
    GoodsEntity goodsMock = Mockito.mock(GoodsEntity.class);
    GoodsEntity goodsSpy = Mockito.spy(GoodsEntity.class);

    System.out.println("goodsMock.finalMethd()1 = " + goodsMock.finalMethd());
    Mockito.when( goodsMock.finalMethd() ).thenReturn( new GoodsEntity("GoodsMock") );
    System.out.println("goodsMock.finalMethd()2 = " + goodsMock.finalMethd());



    System.out.println("goodsSpy.finalMethd()1 = " + goodsSpy.finalMethd());
    Mockito.when( goodsSpy.finalMethd() ).thenReturn( new GoodsEntity("goodsSpy") );
    System.out.println("goodsSpy.finalMethd()2 = " + goodsSpy.finalMethd());
}

final方法的模拟 mock。spy对象对 Mockito.when/Mockito.doReturn都有效

@Test
public void mockFinalMethod2() {
    GoodsEntity goodsMock = Mockito.mock(GoodsEntity.class);
    GoodsEntity goodsSpy = Mockito.spy(GoodsEntity.class);

    System.out.println("goodsMock.finalMethd()1 = " + goodsMock.finalMethd());
    Mockito.doReturn(new GoodsEntity("GoodsMock")).when(goodsMock).finalMethd();
    System.out.println("goodsMock.finalMethd()2 = " + goodsMock.finalMethd());


    System.out.println("goodsSpy.finalMethd()1 = " + goodsSpy.finalMethd());
    Mockito.doReturn(new GoodsEntity("GoodsSpy")).when(goodsSpy).finalMethd();
    System.out.println("goodsSpy.finalMethd()2 = " + goodsSpy.finalMethd());
}

给私有属性设置值(非私有属性也能用)

@Test
public void setField() throws NoSuchFieldException {
    //设置spring的方法
    ReflectionTestUtils.setField(goodsServiceImplSpy,"goodsExMapper",goodsExMapper);

    //mockito 3.3
    //FieldSetter.setField(goodsServiceImplSpy, GoodsServiceImpl.class.getDeclaredField("goodsExMapper"), goodsExMapper);

    //mockito 3.5(用法java的反射,不如直接用spring那个)
    new InstanceField( GoodsServiceImpl.class.getField("goodsExMapper"), goodsServiceImplSpy).set( goodsExMapper );

    System.out.println("goodsServiceImplSpy.add(Goods.randomGoods() ) = " + goodsServiceImplSpy.add(Goods.randomGoods()));
}

模拟静态方法

/**
 * 3.4开始支持,3.4以前需要使用powerMock
 *
 */
@Test
public void mockStaticMethod() {
    Goods goods1 = new Goods();
    goods1.setName("good1");

    //mock所有静态方法,都是返回默认值
    MockedStatic<Goods> goodsMockedStatic = Mockito.mockStatic(Goods.class);

    //模拟Goods.randomGoods方法,指定桩
    goodsMockedStatic.when(Goods::randomGoods).thenReturn( goods1 );

    GoodsEntity goods = GoodsEntity.randomGoods();
    GoodsEntity goods2 = GoodsEntity.randomGoods2();
    System.out.println(JSONUtil.toJsonStr( goods ));
    System.out.println(JSONUtil.toJsonStr( goods2 ));
}

构造方法模拟

@Test
public void mockConstructionMethod() {
    MockedConstruction<Goods> goodsMockedConstruction = Mockito.mockConstruction(Goods.class);
    //模拟构造方法可以关闭
    //goodsMockedConstruction.close();

    Goods goods = new Goods();
    Goods goods2 = new Goods("1");
    System.out.println( goods);
    System.out.println( goods2);
    System.out.println( JSONUtil.toJsonStr(goods));
    System.out.println( JSONUtil.toJsonStr(goods2));
    //goodsMockedConstruction.constructed()) 里面记录着通过 mock构造的对象
    System.out.println( JSONUtil.toJsonStr(goodsMockedConstruction.constructed()));

}

返回值模拟

@Test
public void retuen() {
    GoodsEntity goodsMock = Mockito.mock(GoodsEntity.class);
    GoodsEntity goodsSpy = Mockito.spy(GoodsEntity.class);

    //指定返回值
    Mockito.doReturn("data").when(goodsMock).getData();
    System.out.println( goodsMock.getData() );

    //指定什么也不做
    Mockito.doNothing().when(goodsSpy).setData(Mockito.anyString());
    goodsSpy.setData("a");
    System.out.println( goodsSpy.getData() );

    //调用真实方法
    Mockito.doCallRealMethod().when(goodsMock).setData(Mockito.anyString());
    Mockito.doCallRealMethod().when(goodsMock).getData();
    goodsMock.setData("data2");
    System.out.println( goodsMock.getData() );


    //doAnswer之定义方法mock 过程,和 doReturn只能改返回值
    Mockito.doAnswer(param->{
        System.out.println(JSONUtil.toJsonStr( param.getArguments() ));
        System.out.println(param.getMock());
        return param.getMethod().getName();
    }).when(goodsMock).getData();

    System.out.println( goodsMock.getData() );

    //doAnswer之定义方法mock 过程,和 doReturn只能改返回值
    Mockito.doAnswer(param -> {
        System.out.println(JSONUtil.toJsonStr(param.getArguments()));
        GoodsEntity mock = (GoodsEntity) param.getMock();
        ReflectionTestUtils.setField(mock, "data", param.getArgument(0));
        return param.getMethod().getName();
    }).when(goodsSpy).setData(Mockito.anyString());
    goodsSpy.setData("aaa");
    System.out.println( goodsSpy.getData() );

    //抛出异常
    Mockito.doThrow(new RuntimeException("1111")).when( goodsMock ).getDes();
    //goodsMock.getDes();

}

参数匹配

@Test
public void argumentMatchers() {
    //Mockito 集成了 ArgumentMatchers,里面有很多辅助参数匹配的方法
    //参数匹配是用于对指定参数放回不同的返回值


    GoodsEntity goodsMock = Mockito.mock(GoodsEntity.class);
    GoodsEntity goodsSpy = Mockito.spy(GoodsEntity.class);

    //int匹配
    Mockito.doReturn(1).when(goodsMock).square(ArgumentMatchers.anyInt());
    //空参数匹配
    Mockito.doReturn("1100").when(goodsMock).m2(ArgumentMatchers.isNull());
    //指定在开头(也提供了指定结尾,或者从包含)
    Mockito.doReturn("abc开头").when(goodsMock).m2(ArgumentMatchers.startsWith("abc"));

    //自定义匹配,所有的xxxTHat接口都是可以实现ArgumentMatcher接口,然后自定义匹配规则
    Mockito.doReturn("123开头").when(goodsMock).m2( ArgumentMatchers.argThat(new ArgumentMatcher<String>() {
        @Override
        public boolean matches(String arg) {
            return arg.startsWith("123");
        }
    }) );




    int square = goodsMock.square(10);
    System.out.println( square );

    String rt = goodsMock.m2("1234" );
    System.out.println("rt = " + rt);


}

验证一个方法是否被调用

@Test
public void verify() {
    Goods goodsMock = Mockito.mock(Goods.class);

    goodsMock.getDes();
    //goodsMock.getDes();

    //默认是验证倍调用了一次
    //Mockito.verify(goodsMock).getDes();

    //没有调用
    // Mockito.xxx方法返回 VerificationMode 类型的就是验证用于验证次数的
    //Mockito.verify(goodsMock, Mockito.never()).getDes();

    //至少多少次
    //Mockito.verify(goodsMock, Mockito.atLeastOnce()).getDes();
    //Mockito.verify(goodsMock, Mockito.atLeast(2)).getDes();

    //至多调用2次
    //Mockito.verify(goodsMock, Mockito.atLeast(2)).getDes();


    //5秒以后再验证是否被调用(验证调用一次)
    Mockito.verify(goodsMock, Mockito.after(5000)).getDes();

}

Mockit可以和TestMe之类的IDEA插件配合生成单元测试代码

插件

Mockito用法总结_静态方法

可以直接生成

Mockito用法总结_System_02

选择使用的是Mockito还是powermock,Junit4还是Junit5

Mockito用法总结_静态方法_03

生成的测试代码,生成以后一般需要做一些调整,生成的时候已经对DAO等服务调用模拟了,我们要做的是确认这些方法模拟是我们需要的已经修改对面模拟值。

package com.lomi.service.impl;

import com.lomi.entity.Goods;
import com.lomi.entity.JsonItem;
import com.lomi.entity.in.BatchIn;
import com.lomi.mapper.GoodsExMapper;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Arrays;
import java.util.List;

import static org.mockito.Mockito.*;

public class GoodsServiceImplTest {
    @Mock
    GoodsExMapper goodsExMapper;
    @Mock
    SqlSessionFactory sqlSessionFactory;

    @InjectMocks
    GoodsServiceImpl goodsServiceImpl;

    @Before
    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void testAdd() throws Exception {
        when(goodsExMapper.insert(any(Goods.class))).thenReturn(0);

        goodsServiceImpl.add(new Goods("name"));
    }

    @Test
    public void testAddBatch() throws Exception {
        goodsServiceImpl.addBatch(Arrays.<Goods>asList(new Goods("name")));
        verify(goodsExMapper).addBatch(any(List.class));
    }

    @Test
    public void testAddBatchJson() throws Exception {
        goodsServiceImpl.addBatchJson(Arrays.<JsonItem>asList(new JsonItem()));
        verify(goodsExMapper).addBatchJson(any(List.class));
    }

    @Test(expected = NullPointerException.class)
    public void testAddBatchByExecutorType() throws Exception {
        when(goodsExMapper.insert(any(Goods.class))).thenReturn(0);
        when(sqlSessionFactory.openSession(any(ExecutorType.class))).thenReturn(null);

        goodsServiceImpl.addBatchByExecutorType(new BatchIn());
    }

    @Test(expected=RuntimeException.class)
    public void testTransationKafkaMsg() throws Exception {
        when(goodsExMapper.insert(any(Goods.class))).thenReturn(0);

        goodsServiceImpl.transationKafkaMsg(new Goods("name"), Boolean.TRUE);
    }

    @Test(expected = RuntimeException.class)
    public void testTestRetryable() throws Exception {
        when(goodsExMapper.insert(any(Goods.class))).thenReturn(0);

        goodsServiceImpl.testRetryable(new Goods("name"));
    }

    @Test
    public void testRecover() throws Exception {
        goodsServiceImpl.recover(new Exception("message", new Throwable("message")), new Goods("name"));
    }
}

多次插桩

/**
 * 多次插桩,会被覆盖,如果需要依次放回多个值,需要在一次插桩的时候指定
 */
@Test
public void n() {
    Goods goodsMock = Mockito.mock(Goods.class);
    Mockito.doReturn("1","2").doReturn("3").when(goodsMock).getDes();
    Mockito.when( goodsMock.getDes()).thenReturn("a","b").thenReturn("c");


    //前三次分别返回a,b,c,之后全是放回c
    System.out.println( goodsMock.getDes() );
    System.out.println( goodsMock.getDes() );
    System.out.println( goodsMock.getDes() );
    System.out.println( goodsMock.getDes() );
    System.out.println( goodsMock.getDes() );
    System.out.println( goodsMock.getDes() );
}

异常处理方法

@Rule
    public  ExpectedException exception = ExpectedException.none();
    /**
     * 异常处理方法1
     */
    @Test
    public void e1() {
        exception.expect(RuntimeException.class);
        exception.expectMessage("Runtime exception occurred");
        throw new RuntimeException("Runtime exception occurred");
    }

    /**
     * 异常处理方法2
     */
    @Test(expected = Exception.class)
    public void e2() {
        ExpectedException exception = ExpectedException.none();
        exception.expect(Exception.class);
    }

PowerMock静态方法模拟

@RunWith(PowerMockRunner.class)和相应的@PrepareForTest注解。

@RunWith(PowerMockRunner.class)
@PrepareForTest(StaticClass.class)
public class StaticMethodTest {

    @Test
    public void testStaticMethod() throws Exception {
        // 配置静态方法的模拟行为
        PowerMockito.mockStatic(StaticClass.class);
        when(StaticClass.someStaticMethod()).thenReturn("mockedValue");

        // 调用依赖于静态方法的代码并验证其行为
        MyClass myClass = new MyClass();
        String result = myClass.methodUnderTest();

        assertEquals("mockedValue", result);

        // 验证静态方法是否被正确调用
        verifyStatic(StaticClass.class);
        StaticClass.someStaticMethod();
    }
}

其他的一些说明

  • 参数匹配的时候Mockito.anyxxx不包括null
  • Mockito.verify方法只是验证方法是否被调用了指定次数,不是调用
  • Mockito.whe和Mockito.doReturn用于模拟方法
  • Mockito.doThrow用于模拟抛出异常
  • void 返回值 使用 Mockito.doNothing 方法
  • thenAnswer里面可以自定义模拟方法返回值和调用过程
  • Mockito 继承了 ArgumentMatchers,里面有很多辅助参数匹配的方法
  • Mockito.xxx方法返回 VerificationMode 类型的就是验证用于验证次数的
  • 插桩了,不调用会报错?
    貌似有的版本会报错,我用的4.8不会
  • @SpyBean 和 @MockBean spring 容器用
    如果是@RunWith(SpringRunner.class)方式的spring环境运行,@SpyBean,@MockBean会用模拟对象替换spring容器里面的对象。

标签:总结,Goods,Mockito,goodsMock,println,用法,class,out
From: https://blog.51cto.com/cxygg/11917280

相关文章

  • js async/await 用法
    1.使用async/await可以更好地控制事件循环,像处理DOM事件或定时器等场合。eg1......
  • 杂题总结
    杂题总结记号约定不难注意意味着我在初见的时候想到了难以注意意味着我没有注意到P8421[THUPC2022决赛]rsraogpsP8421[THUPC2022决赛]rsraogps-洛谷|计算机科学教育新生态(luogu.com.cn)首先套路性扫描线,然后这个问题就变成增量构造。不难注意不知怎么......
  • LLM大模型基础知识学习总结
    大家好,我是Edison。在这个已经被大模型包围的时代,不了解一点大模型的基础知识和相关概念,可能出去聊天都接不上话。刚好近期我也一直在用GPT和GitHubCopilot,也刚好对这些基础知识很感兴趣,于是学习了一下,做了如下的整理总结,分享与你!一句话描述GPTGPT:GenerativePre-TrainingTra......
  • 八股文总结
    项目八股面经简历整理抽奖大转盘一般项目中都是MVC,做起来简单,随着项目变大,service层,很多不同业务的接口和实现类,导致项目结构很乱,dto,vo系统内部数据流转类,维护和迭代不太好做,DDD在一定程度上解决了这个问题,domain领域层,把不同业务以合理的角度区分为不同的领域,也就是不同包,不......
  • videoPlayer插件的用法
    文章目录1.概念介绍2.使用方法2.1实现步骤2.2具体细节3.示例代码4.内容总结我们在上一章回中介绍了"如何获取文件类型"相关的内容,本章回中将介绍如何播放视频.闲话休提,让我们一起TalkFlutter吧。1.概念介绍播放视频是我们常用的功能,不过Flutter官方SDK......
  • Hadoop 第七周总结
    Hadoop第七周总结在第七周的学习中,我深入探讨了Hadoop生态系统中的几个关键组成部分,重点包括HadoopMapReduce、HDFS(HadoopDistributedFileSystem)、YARN(YetAnotherResourceNegotiator),以及Hadoop的调优策略。以下是本周学习的主要内容和总结:1.HadoopMapReduceMapReduce......
  • Hadoop 第八周总结
    Hadoop第八周总结在第八周的学习中,我进一步探索了Hadoop生态系统的高级功能和工具,主要集中在Hadoop的优化技巧、数据处理框架的整合以及大数据应用的实际案例。以下是本周学习的主要内容和总结:1.Hadoop的性能优化在处理大规模数据时,性能优化至关重要。本周我深......
  • ‍️ SpringBoot中MongoDB的骚操作用法
    不知道大家在工作项目中有没有使用MongoDB,在哪些场景中使用。MongoDB作为NoSQL数据库,不像SQL数据库那样,可以使用Mybatis框架。如果需要在SpringBoot中使用MongoDB的话,我目前知道有三种方式,第一种是直接使用MongoDB官方的SDK,第二种是使用SpringJpa的方式,第三种是使用MongoTemplate......
  • 8.30 上午 becoder 模拟赛总结 & 题解
    T1密码当时想到解法了,却依然认为自己不会做,我真是个人才。结论:对于$\foralli\in[1,n)$,满足密码不是$a_i$的因数,且密码是$a_k$的因数,设满足条件的最小值为$g$则答案为$\frac{n}{g}$。一种最好想的做法:枚举$\gcd(a_k,n)$的因数作为$g$,并枚举$i\in[1,n)$,判断是......
  • 8.31 上午 becoder 模拟赛总结 & 题解
    T1四个质数的和赛场亲测搜索+小剪枝可以得到70pts。考虑$O(p(V)^2)$枚举任意两个质数的和,其中$p(V)$表示$V$以内质数的个数。然后开个数组记录下对于每种和的记录有多少种情况,查询时for循环扫一遍即可,详见代码。复杂度去掉质数筛$O(p(V)^2+tn)$,代码贴在下面(100pts)......