首页 > 其他分享 >单元测试与Mockito

单元测试与Mockito

时间:2023-04-22 18:56:31浏览次数:43  
标签:OtherClient Mockito 单元测试 client query mock

系列文章目录和关于我

零丶背景

最近在新公司第一次上手写代码,写了一个不是很难的业务逻辑代码,但是在我写单元测试的时候,发现自己对单元测试的理解的就是一坨,整个过程写得慢,还写得臭。造成这种局面我认为是因为:

  • 对Mockito api是不是很熟悉
  • 没有自己单元测试方法论,不知道怎样写好单元测试。

now,我将从这两个部分来学习一下单元测试,如何写,如何写好单元测试?

一丶为什么需要单元测试

在上一份工作,我基本上不咋写单元测试,觉得很麻烦,不如直接postman,swagger开冲,这种显然不容易覆盖到所有的case。

单元测试的好处:

  • 增强信心

    单元测试覆盖率越高,我们越对自己的代码有信心。

  • 揭示意图

    写单元测试的时候,我们是明确自己的代码到底是出于什么目的写的

  • 安全重构

    不只是重构,哪怕后续在原有功能上进行添加,通过执行之前存在单元测试有助于我们验证,我们没有影响到原有功能。

  • 快速反馈

    写单元测试的过程,我们其实有可能发现自己代码存在的缺陷,通过单元测试直白的报错,我们可以很快得到反馈,这个反馈速度是测试滴滴你所不具备的。

  • 定位缺陷

    单元测试并不能帮我们找出所有存在的bug(测试同事:没事,我会出手),但是我们发现bug后,可以将输入放在单元测试中进行回放,直到可以重现并定位到问题,然后使用这种情况的case来补充单元测试用例。

二丶引入依赖&这些依赖的作用

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.13.2</version>
   <scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
   <groupId>org.mockito</groupId>
   <artifactId>mockito-core</artifactId>
   <version>5.3.1</version>
   <scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
   <groupId>org.mockito</groupId>
   <artifactId>mockito-inline</artifactId>
   <version>3.7.7</version>
   <scope>test</scope>
</dependency>
  • junit

    提供了许多方便使用的注解,标注在方法上

    image-20230422155916305

  • Mockito

    Mockito 是一种 Java Mock 框架,主要就是用来做 Mock 测试的,可以模拟出一个对象、模拟方法的返回值、模拟抛出异常,模拟静态方法等等,同时也会记录调用这些模拟方法的参数、调用顺序,从而可以校验出这个 Mock 对象是否有被正确的顺序调用,以及按照期望的参数被调用。

    Mock 测试:比如我们的Service依赖其他的服务提供的接口方法,使用mock可以模拟出这个接口的表现(正常返回,抛出异常等到)从而让单元测试不那么依赖外部的服务。

  • powermock

    可以看作是mock增强版本,提供模拟私有方法等功能,我们这里没有进行引入。

三丶Mockito 常用功能

0.从一个例子开始

image-20230422165535432

image-20230422165608337

如上图,我们的MyService依赖于OtherClient,这个OtherClient可能由于网络原因会出现错误,或者其他情况抛出异常,我们的MyService需要进行处理。

1.@InjectMocks & @Mock &MockitoAnnotations.openMocks

image-20230422165957197

  • @InjectMocks:标记应进行注射的字段,类似于spring的依赖注入,但是这里会使用Mock产生的对象
  • @Mock :将字段标记为模拟字段,我们可以使用Mockito提供的方法来 打桩
  • MockitoAnnotations.openMocks:开启Mockito注解的功能

2.打桩

打桩可以理解为 mock 对象规定它的行为,使其按照我们的要求来执行具体的操作。

2.1 指定入参让mock对象返回指定对象——thenReturn

//让client在query入参为1的时候,返回100为key,aaa为value的单键值对的map
Mockito.when(client.query(1)).thenReturn(new HashMap<>(Collections.singletonMap(100, "aaaa")));
Map<Integer, String> res = client.query(1);
Assert.assertEquals(1, res.size());
Assert.assertEquals(res.get(100), "aaaa");

2.2 指定入参让mock对象抛出异常——thenThrow

Mockito.when(client.query(2)).thenThrow(new RuntimeException("222"));
Assert.assertThrows("222", RuntimeException.class, () -> client.query(2));

2.3 指定任何参数都执行指定操作——Mockito.anyInt()

Mockito.when(client.query(Mockito.anyInt())).thenReturn(new HashMap<>());
Assert.assertEquals(0, client.query(-1).size());

2.4 参数匹配器——ArgumentMatcher

有时候,我们希望入参入参符合要的时候,mock对象进行什么操作。

如下,我们要求mock对象在输入参数是1, 2, 3的时候返回空map

HashSet<Integer> integers = new HashSet<>(Arrays.asList(1, 2, 3));
Mockito.when(client.query(Mockito.argThat(new ArgumentMatcher<Integer>() {
    @Override
    public boolean matches(Integer argument) {
        return integers.contains(argument);
    }
}))).thenReturn(Collections.emptyMap());
Assert.assertEquals(0,client.query(2).size());

2.5 控制mock对象返回结果——thenAnswer

有时候我们希望mock对象可以根据输出的不同返回不同的结果,符合我们要求的结果。

如下,我们使用thenAnswer根据入参返回不同的结果。

Mockito.when(client.query(Mockito.anyInt())).thenAnswer(new Answer<Object>() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        Integer argument = invocation.getArgument(0);
        String str = argument%2==0?"偶数":"奇数";
        return new HashMap<Integer,String>(Collections.singletonMap(argument,str));
    }
});
Assert.assertEquals("偶数", client.query(2).get(2));

2.6 让mock对象调用真实方法——thenCallRealMethod

上面都是说mock对象如何去控制输出,thenCallRealMethod可以让mock对象执行真实的逻辑。

Mockito.when(client.query(-1)).thenCallRealMethod();

2.7 验证——verify

verify可以让我们验证当前mock对象,比如下面验证client至少执行了四次query

//验证 client.query最起码调用了4次
Mockito.verify(client,Mockito.atLeast(4)).query(Mockito.anyInt());

2.8 mock静态方法——mockStatic

有时候静态方法也需要进行mock控制,可以使用

image-20230422175552018

四丶一个有依赖的单元测试

0.还是这个例子

image-20230422165535432

image-20230422165608337

如上图,我们的MyService依赖于OtherClient,这个OtherClient可能由于网络原因会出现错误,或者其他情况抛出异常,我们的MyService需要进行处理。

1.确认需要mock什么

上面这个例子中,OtherClient是外部提供给我们的接口,它存在一定的机率失败,在单元测试的过程我们需要mock它的行为,而不是真的去调用外部接口。

2.定义对象,前置准备

image-20230422182821922

这里我们得明确 MyService是我们需要测试的,那就别mock它,OtherClient是外部依赖,需要进行mock控制其行为。

3.1mock方法->调用方法->验证方法

3.1 模拟OtherClient抛出异常

image-20230422183255421

3.2 模拟OtherClient返回空Map

image-20230422183622488

3.3模拟OtherClient返回非空Map

image-20230422184443824

标签:OtherClient,Mockito,单元测试,client,query,mock
From: https://www.cnblogs.com/cuzzz/p/17343686.html

相关文章

  • 用mockito来验证方法传参
    @CaptorprivateArgumentCaptor<Map<String,String>>captor;@Beforepublicvoidinit(){MockitoAnnotations.initMocks(this);}publicvoidtest(){//somecodesverify(mockAlarmUpdater,times(1))......
  • 05.单元测试、注解和反射
    1、单元测试什么是单元测试?单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。目前测试方法是怎么进行的,存在什么问题?只有一个main方法,如果一个方法的测试失败了,其他方法测试会受到影响......
  • 解决Spring boot 单元测试,无法读取配置文件问题。
    1.启动类上加上@EnableConfigurationProperties2.springboot版本springboot2.X版本在单元测试中读取不到yml配置文件的值这是个大坑,在项目中写单元测试的时候需要读取一个yml配置文件的值,发现无论如何都读取不到,后来发现了这个坑。改成properties就行了。或者增加@RunWith(Spr......
  • Java SpringBoot Test 单元测试中包括多线程时,没跑完就结束了
    如何阻止JavaSpringBootTest单元测试中包括多线程时,没跑完就结束了使用CountDownLatchCountDownLatch、CyclicBarrier使用区别多线程ThreadPoolTaskExecutor应用JavaBasePooledObjectFactory对象池化技术@SpringBootTestpublicclassPoolTest{@Testvoid......
  • 基于SpringBoot实现单元测试的多种情境/方法(二)
    本文分享自天翼云开发者社区@《基于SpringBoot实现单元测试的多种情境/方法(二)》,  作者:才开始学技术的小白 1Mock基础回顾在上一篇分享中我们详细介绍了简单的、用mock来模拟接口测试环境的方法,具体的使用样例我们再回顾一下:1.首先是最简单的不需要传参的示例,需要注意的是,......
  • 单元测试
    一。使用Mockito<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.8.0</version><scope>test</scope></dependency>@RunWith(MockitoJUnitRunne......
  • 单元测试案例
    在日常的springboot项目开发中,总会需要写一些单元测试用例,一些单元测试的方法用的比较少,编写时又需要去查询,因此在此总结一些测试案例Junit是目前主流的单元测试框架,我,常用的为Junit4,以下测试案例是基于Junit4来编写代码地址:https://github.com/x104859/test-case单元测试的目......
  • Python——单元测试的实现
    摘要单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。在软件开中的测试是很重要的一部分。python测试相关库unittest,内置库,模仿PyUnit写的,简洁易用,缺点是比较繁琐。nose,测试发现,发现并运行测试。pytest,笔者目前喜欢用这个,写起来很方便,并且很多知名开源项......
  • Java SpringBoot Test 单元测试中包括多线程时,没跑完就结束了
    如何阻止JavaSpringBootTest单元测试中包括多线程时,没跑完就结束了使用CountDownLatchCountDownLatch、CyclicBarrier使用区别多线程ThreadPoolTaskExecutor应用JavaBasePooledObjectFactory对象池化技术@SpringBootTestpublicclassPoolTest{@Test......
  • ChatGPT如何帮助编写代码文档和单元测试
    ChatGPT如何帮助编写代码文档和单元测试    有多少次你专注于编程而忘记了写函数、方法、类的非常简单的代码文档?我不是在问单元测试.直到我发现ChatGPT可以做到这一点:除了代码文档,它在编写单元测试方面也做得很好。此外,在最后,我可以要求他为其他想使用我的代码的贡献者生......