Spring Boot集成单元测试之如何mock

这篇具有很好参考价值的文章主要介绍了Spring Boot集成单元测试之如何mock。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、单元测试

springboot集成mock,测试,单元测试,log4j
前言

我们在购买电脑的时候,一次就可以开机了,这是因为在出厂的时候厂家就帮我们做好了测试。那如果没有厂家这步,我们会面临显示器无法显示的问题,磁盘无法挂载等情况。那运气好可能一次就能定位,运气不好,还需要排查显示器,内存条,主板,显卡等一系列组件。等我们排查就花费了大量的时间和精力。那如果在组装之前就测试好了每个组件情况,也就能避免这样的事情发生了。如果把电脑的生产,测试和软件的开发测试类比,就会发现。
显卡,内存条就像是软件中的单元,通常是函数或者类,对单个元器件的测试就像是软件测试中的单元测试;
组装完成的功能机箱,显示器就像是软件中的模块,对机箱显示器的测试就像是软件中的集成测试; 电脑全部组装完成就像是软件完成了预发布版本。
电脑全部组装完成后的开机测试就像是软件中的系统测试。

1.1、定义

单元测试是指对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常是指函数或者类。

  • 驱动代码是用来调用被测函数的,而桩代码和 Mock 代码是用来代替被测函数调用的真实代码的。

  • Stub(桩对象):Stub通常用于替代测试对象的某些部分,以便进行单元测试等测试。例如,当被测代码需要访问外部数据源或者调用其他函数时,我们可以使用Stub来模拟这些依赖项的行为,从而使得测试过程更加独立和可控。

  • Mock(模拟对象):Mock通常用于模拟函数或对象的行为,以便更好地进行单元测试或功能测试。例如,当被测代码需要与某个对象进行交互时,我们可以使用Mock来模拟该对象的行为和响应,并判断被测代码的行为是否正确。

1.2、作用

一般测试方法如下:

  • 启动整个应用,模拟用户正常操作。设计到大量的改动需要再次模拟场景。
  • 代码某个地方写一个临时入口(比如main),模拟调用。临时代码用后需要删除。

当有如下场景的时候就可以考虑采用单元测试

  • 测试场景较多,且需要多次场景测试。(比如每次改动一个点需要多次调用模拟场景,这里适合做成自动化。)
  • 被测单元依赖的模块尚未开发完成A,而被测单元需要依赖模块的返回值进行后续处理。
  • 需要将当前被测单元和其依赖模块独立开来,构造一个独立的测试环境,不关注被测单元的依赖对象,只关注被测单元的功能逻辑。(比如新增了逻辑不需要测试整个流程,只需要测试修改部分逻辑)
  • 被测单元依赖的对象较难模拟或者构造比较复杂。(比如db连接池)

1.3、使用

1.3.1、 常用注解

  • @SpringBootTest:获取启动类,加载配置,寻找主配置启动类(被 @SpringBootApplication 注解

  • @RunWith(SpringRunner.class):让JUnit运行Spring的测试环境,获得Spring环境的上下文的支持

  • @Test:测试方法,可以测试期望异常(配置expected )和超时时间。

  • @Mock :是 Mockito.mock() 方法的简写。创建的是全部mock的对象,即在对具体的方法打桩之前,mock对象的所有属性和方法全被置空(0或null)。

  • @Spy:会调用真实的方法,有返回值的调用真实方法并返回真实值;如果发现修饰的变量是 null,会自动调用类的无参构造函数来初始化。定义了mock方法的则执行mock(即虚假函数);默认生成后所有依赖的对象都会null,且要一个无参构造。

  • @InjectMocks :创建一个实例,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。如果使用spring的@Autowired注解一起使用,则会直接使用spring容器的对象,并将@Mock(或@Spy)对象注入。

  • @MockBean : Spring Boot 中的注解。我们可以使用 @MockBean 将 mock 对象添加到 Spring 应用程序上下文中。该 mock 对象将替换应用程序上下文中任何现有的相同类型的 bean。如果应用程序上下文中没有相同类型的 bean,它将使用 mock 的对象作为 bean 添加到上下文中。

  • @SpyBean:同上。

1.3.2、 注意事项

  1. @InjectMocks由mock框架管理,所以只能注入@Mock和@Spy的对象。
@Mock
AService aService;

@InjectMocks
AController aController;  //这里会注aService

@Autowired
AController aController;//这里不会注aService

class BController{
    AService aService;
}
  1. @MockBean和@SpyBean由spring管理,会替换上下文相同对象。
@MockBean
AService aService;

@Autowired
AController aController; //这里会注入aService
  1. 如果想一个spring对象注入mock框架的对象,可通过@InjectMocks桥接。
@Mock
AService aService;

@Autowired
@InjectMocks
AController aController;//这里会注入aService
  1. @SpyBean存在循环依赖问题,其原因主要是早期暴露和正常暴露会创建不同对象,造成对象不一致。通过如下方式也没办法解决,因为spy的是spring增强的对象,而不是像@SpyBean注解代理的是原生对象。
AService bean = context.getBean(AService.class);
AService spy = Mockito.spy(bean);
  1. 设置 spy 逻辑时不能再使用 when(某对象.某方法).thenReturn(某对象) 的语法,而是需要使用 doReturn(某对象).when(某对象).某方法 或者 doNothing(某对象).when(某对象).某方法

  2. 对于 static 、 final 、private修饰的方法和equals()、hashCode()方法, Mockito 无法对其进行when(…).thenReturn(…) 操作。

1.3.4、 注解使用场景

  • 非spring环境:@Mock+@Spy+@InjectMocks
  • spring环境:@MockBean+@SpyBean+@Autowired,为测试主体类部分打桩考虑使用SpyBean, 为外部依赖打桩,考虑使用MockBean

测试代码

@Service
public class AService {
public String hasReturnAndArgs(String str){
    return "10";
}
public String hasReturn(){
    return "10";
}

public void hasArgs(String str){
    System.out.println(1000);
}
public void noArgs(){
    System.out.println(1000);
}
}

@RestController
public class AController {
    @Autowired
    private AService aService;
public String hasReturnAndArgs(String str){
    return aService.hasReturnAndArgs(str);
}
public String hasReturn(){
    return aService.hasReturn();
}

public void hasArgs(String str){
    aService.hasArgs(str);
}
public void noArgs(){
    aService.noArgs();
}
}

1.3.5、使用mock

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ServiceTest {
    @Before
    public void before() {
        // 启用 Mockito 注解
        MockitoAnnotations.initMocks(this);
    }
    
    @Mock
    AService aService;
    @InjectMocks
    AController aController;
    @Test
    public void test() {
        //1.不调用真实方法,默认返回null
        String value = aService.hasReturnAndArgs("10");
        Assert.assertEquals(value, null);

        //2.打桩
        //当传参是10L时,返回response
        Mockito.when(aService.hasReturnAndArgs("10")).thenReturn("30");
        //当传参是20L时,真实调用
        Mockito.when(aService.hasReturnAndArgs("20")).thenCallRealMethod();
        //当传参是30L时,抛出异常
        Mockito.when(aService.hasReturnAndArgs("30")).thenThrow(new Exception("test error"));

        Assert.assertEquals(aService.hasReturnAndArgs("10"), "30");
        //入口为真实方法,内部mock对象调用的也是mock方法
        Assert.assertNotEquals(aService.hasReturnAndArgs("20"), "30");
        try {
            Assert.assertNotEquals(aService.hasReturnAndArgs("30"), "30");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        //3.注入对象
        Assert.assertEquals(aController.hasReturnAndArgs("10"), "30");
    }
}

1.3.6、使用Spy

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ServiceTest {
    @Before
    public void before() {
        // 启用 Mockito 注解
        MockitoAnnotations.initMocks(this);
    }
 
    @Spy
    AService spy;
    @Test
    public void test() {
        //AService spyTemp = new AService();
        //AService spy = Mockito.spy(spyTemp);

        //1.调用真实方法
        Assert.assertEquals(spy.hasReturnAndArgs("20"), "10");

        //2.打桩
        Mockito.doReturn("30").when(spy).hasReturnAndArgs("20");
        Assert.assertEquals(spy.hasReturnAndArgs("20"), "30");
        //验证是否被调用了一次
        Mockito.verify(spy,times(1)).hasReturnAndArgs("20");


        //设置任何hasReturnAndArgs调用都返回30
        Mockito.doReturn("30").when(spy).hasReturnAndArgs(Mockito.anyString());
        Assert.assertEquals( spy.hasReturnAndArgs("-2"), "30");
        Mockito.verify(spy,times(2)).hasReturnAndArgs(Mockito.anyString());



        //不支持这样
        Mockito.when(spy.hasReturnAndArgs("20")).thenReturn("10");
        Assert.assertEquals(spy.hasReturnAndArgs("20"), "10");
    }
}

1.3.7、使用spring集成

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ServiceTest {
    @Before
    public void before() {
        // 启用 Mockito 注解
        MockitoAnnotations.initMocks(this);
    }
  
    @SpyBean
    private AService spy;
    @Autowired
    AController aController;
    @Test
    public void test() {
        //调用真实方法
        Assert.assertEquals(spy.hasReturnAndArgs("20"), "10");
        Mockito.doReturn("30").when(spy).hasReturnAndArgs(Mockito.anyString());
        Assert.assertEquals(spy.hasReturnAndArgs("20"), "30");
        Mockito.verify(spy,times(1)).hasReturnAndArgs(Mockito.anyString());
        Assert.assertEquals(aController.hasReturnAndArgs("20"), "30");
    }
}

1.3.8、行为验证

        //是否调用过一次
        Mockito.verify(spy).hasReturnAndArgs(Mockito.anyString());
        //是否调用过N次
        Mockito.verify(spy,times(1)).hasReturnAndArgs(Mockito.anyString());
        //没有被调用,相当于 times(0)
        Mockito.verify(spy,never()).hasReturnAndArgs(Mockito.anyString());
        //atLeast(N) 至少被调用 N 次
        //atLeastOnce() 相当于 atLeast(1)
        //atMost(N) 最多被调用 N 次

1.3.9、断言

  @Test
    public void testAssert() {


        // allOf: 所有条件都必须满足,相当于&&
        assertThat("myname", allOf(startsWith("my"), containsString("name")));

        //  anyOf: 其中一个满足就通过, 相当于||

        assertThat("myname", anyOf(startsWith("na"), containsString("name")));

        //  both: &&

        assertThat("myname", both(containsString("my")).and(containsString("me")));

        //  either: 两者之一
        assertThat("myname", either(containsString("my")).or(containsString("you")));

        //  everyItem: 每个元素都需满足特定条件
        assertThat(Arrays.asList("my", "mine"), everyItem(startsWith("m")));

        //  hasItem: 是否有这个元素
        assertThat(Arrays.asList("my", "mine"), hasItem("my"));

        //  hasItems: 包含多个元素
        assertThat(Arrays.asList("my", "mine", "your"), hasItems("your", "my"));
        //  is: is(equalTo(x))或is(instanceOf(clazz.class))的简写
        assertThat("myname", is("myname"));
        assertThat("mynmae", is(String.class));

        //  anything(): 任何情况下,都匹配正确
        assertThat("myname", anything());

        //  not: 否为真,相当于!
        assertThat("myname", is(not("you")));

        //  nullValue(): 值为空
        String str = null;
        assertThat(str, is(nullValue()));

        //  notNullValue(): 值不为空
        String str2 = "123";

        assertThat(str2, is(notNullValue()));

//  -------------------------字符串匹配
        //  containsString:包含字符串
        assertThat("myname", containsString("na"));
        //  stringContainsInOrder: 顺序包含,“my”必须在“me”前面
        assertThat("myname", stringContainsInOrder(Arrays.asList("my", "me")));
        //  endsWith: 后缀
        assertThat("myname", endsWith("me"));
        //  startsWith: 前缀
        assertThat("myname", startsWith("my"));
        //  isEmptyString(): 空字符串
        assertThat("", isEmptyString());
        //  equalTo: 值相等, Object.equals(Object)
        assertThat("myname", equalTo("myname"));
        assertThat(new String[]{"a", "b"}, equalTo(new String[]{"a", "b"}));
        //  equalToIgnoringCase: 比较时,忽略大小写
        assertThat("myname", equalToIgnoringCase("MYNAME"));
        //  equalToIgnoringWhiteSpace: 比较时, 首尾空格忽略, 比较时中间用单个空格
        assertThat(" my \t name ", equalToIgnoringWhiteSpace(" my name "));
        //  isOneOf: 是否为其中之一
        assertThat("myname", isOneOf("myname", "yourname"));
        //  isIn: 是否为其成员
        assertThat("myname", isIn(new String[]{"myname", "yourname"}));
        //  toString() 返回值校验
        assertThat(333, hasToString(equalTo("333")));

//------------------------  数值匹配
        //  closeTo: [operand-error, operand+error], Double或BigDecimal类型
        assertThat(3.14, closeTo(3, 0.5));
         assertThat(new BigDecimal("3.14"), is(BigDecimalCloseTo.closeTo(new BigDecimal("3"), new BigDecimal("0.5"))));
        //  comparesEqualTo: compareTo比较值
        assertThat(2, comparesEqualTo(2));
        //  greaterThan: 大于
        assertThat(2, greaterThan(0));
        //  greaterThanOrEqualTo: 大于等于
        assertThat(2, greaterThanOrEqualTo(2));
        //  lessThan: 小于
        assertThat(0, lessThan(2));
        //  lessThanOrEqualTo: 小于等于
        assertThat(0, lessThanOrEqualTo(0));

//  -----------------------------------------------------集合匹配
        // array: 数组长度相等且对应元素也相等
        assertThat(new Integer[]{1, 2, 3}, is(array(equalTo(1), equalTo(2), equalTo(3))));
        // hasItemInArray: 数组是否包含特定元素
        assertThat(new String[]{"my", "you"}, hasItemInArray(startsWith("y")));
        // arrayContainingInAnyOrder, 顺序无关,长度要一致
        assertThat(new String[]{"my", "you"}, arrayContainingInAnyOrder("you", "my"));
        // arrayContaining:  顺序,长度一致
        assertThat(new String[]{"my", "you"}, arrayContaining("my", "you"));
        // arrayWithSize: 数组长度
        assertThat(new String[]{"my", "you"}, arrayWithSize(2));
        // emptyArray: 空数组
        assertThat(new String[0], emptyArray());
        // hasSize: 集合大小
        assertThat(Arrays.asList("my", "you"), hasSize(equalTo(2)));
        // empty: 空集合
        assertThat(new ArrayList<String>(), is(empty()));
        //  isIn: 是否为集合成员
        assertThat("myname", isIn(Arrays.asList("myname", "yourname")));

//  -------------------------------------------------Map匹配
        Map<String, String> myMap = new HashMap();
        myMap.put("name", "john");
        //  hasEntry: key && value匹配
        assertThat(myMap, hasEntry("name", "john"));
        //  hasKey: key匹配
        assertThat(myMap, hasKey(equalTo("name")));
        //  hasValue: value匹配
        assertThat(myMap, hasValue("john"));

    }

1.3.10、常用方法

               //有参有返回
        Mockito.doReturn("haha").when(spy).hasReturnAndArgs(Mockito.anyString());
        Assert.assertEquals(spy.hasReturnAndArgs("str"), "haha");
        //无参有返回
        Mockito.doReturn("hasReturn").when(spy).hasReturn();
        Assert.assertEquals(spy.hasReturn(), "hasReturn");
        //有参无返回
        Mockito.doNothing().when(spy).hasArgs(Mockito.anyString());
        spy.hasArgs("haha");
        Mockito.verify(spy).hasArgs(Mockito.anyString());
        //无参无返回
        Mockito.doNothing().when(spy).noArgs();
        spy.noArgs();
        Mockito.verify(spy).noArgs();
        //调用真实方法
        Mockito.doCallRealMethod().when(spy).hasReturnAndArgs("hha");
        Assert.assertEquals(spy.hasReturnAndArgs("hha"),"10");
        //静态方法

        //抛出异常
        Mockito.doThrow(new RuntimeException()).when(spy).hasReturnAndArgs(eq("20"));
        try {
            spy.hasReturnAndArgs("20");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        //---------------------------------------------------------其他
        //参数匹配器
        //anyInt()、anyString()、anyDouble()、anyList()、anyMap(),eq(1)
        Mockito.doReturn("haha").when(spy).hasReturnAndArgs(eq("20"));

        //模拟返回值
        Mockito.doAnswer(invocation -> {
            //获取参数值
            Object[] args = invocation.getArguments();
            String arg = (String) args[0];
            if ("hallo".equals(arg)){
                return "helloWorld";
            }
            return arg;
        }).when(spy).hasReturnAndArgs(Mockito.anyString());
        Assert.assertEquals(spy.hasReturnAndArgs("hallo"), "helloWorld");
        Assert.assertEquals(spy.hasReturnAndArgs("ha"), "ha");

1.3.11、mock静态方法

导入pom

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>4.0.0</version>
        </dependency>
        MockitoAnnotations.initMocks(this);
        Mockito.mockStatic(XXX.class).when(XXX::getXXX)
                .thenReturn("xxx");
                
                //如果用多次需要关闭
        try(MockedStatic<XXX> xx= Mockito.mockStatic(XXX.class)) {
            xx.when(() -> A.b(params)).thenReturn(null);
        }

1.4、统计覆盖率

springboot集成mock,测试,单元测试,log4j

红色为尚未覆盖的行,绿色为覆盖的行。class,method,line分别表示类/方法/行代码测试覆盖率。

springboot集成mock,测试,单元测试,log4j文章来源地址https://www.toymoban.com/news/detail-848651.html

到了这里,关于Spring Boot集成单元测试之如何mock的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot集成单元测试调用dao,service

    温馨提示:本人开发上线视频网站,有想要的看视频的,可以看看。小松鼠

    2024年02月14日
    浏览(42)
  • Spring Boot与微服务测试:JUnit和Mockito的单元和集成测试实践

    微服务架构的流行使得测试变得更为重要。在Spring Boot应用中,使用JUnit和Mockito进行单元和集成测试是一种常见的实践。本文将深入探讨如何利用这两个测试框架,确保Spring Boot微服务的可靠性和稳定性。 单元测试 1.1 JUnit简介 JUnit是Java中最为流行的单元测试框架之一,它提供

    2024年02月21日
    浏览(55)
  • Spring Boot中如何编写优雅的单元测试

    单元测试是指对软件中的最小可测试单元进行检查和验证。在Java中,单元测试的最小单元是类。通过编写针对类或方法的小段代码,来检验被测代码是否符合预期结果或行为。执行单元测试可以帮助开发者验证代码是否正确实现了功能需求,以及是否能够适应应用环境或需求

    2024年02月11日
    浏览(50)
  • Spring 官方建议的在 Spring Boot 应用中如何做单元测试

    Spring Boot 提供了丰富的测试功能,主要由以下两个模块组成: ● spring-boot-test:提供测试核心功能。 ● spring-boot-test-autoconfigure:提供对测试的自动配置。 Spring Boot 提供了一个 spring-boot-starter-test一站式启动器,如以下依赖配置所示。 测试启动器依赖不仅包含以上两个 Spring

    2024年02月08日
    浏览(46)
  • 如何创建自己的Spring Boot Starter并为其编写单元测试

    当我们想要封装一些自定义功能给别人使用的时候,创建Spring Boot Starter的形式是最好的实现方式。如果您还不会构建自己的Spring Boot Starter的话,本文将带你一起创建一个自己的Spring Boot Starter。 创建一个新的 Maven 项目。第三方封装的命名格式是 xxx-spring-boot-starter ,例如:

    2024年03月15日
    浏览(41)
  • Service层代码单元测试以及单元测试如何Mock

    接着上一篇文章:单元测试入门篇,本篇文章作为单元测试的进阶篇,主要介绍如何对Springboot Service层代码做单元测试,以及单元测试中涉及外调服务时,如何通过Mock完成测试。 现在项目都流行前后端代码分离,后端使用springboot框架,在service层编写接口代码实现逻辑。假设

    2023年04月08日
    浏览(50)
  • 在Java微服务项目中,如何使用Mock来进行单元测试?

    摘要: 在系统开发的过程中,单元测试是其中的一个重要环节。在Java微服务项目中,Spring框架本身就为我们提供了一套单元测试的框架SpringBootTest。如果我们在学校完成课堂作业或出于兴趣爱好自学,是可以使用Spring自带的单元测试框架进行单测的。 工作中,这种通过Spri

    2024年02月16日
    浏览(37)
  • 单元测试之 - Spring框架提供的单元/集成测试注解

    Spring框架提供了很多注解来辅助完成单元测试和集成测试(备注:这里的集成测试指容器内部的集成测试,非系统间的集成测试),先看看Spring框架提供了哪些注解以及对应的作用。 @RunWith(SpringRunner.class) / @ExtendWith(SpringExtension.class) : 用于在测试类中启用 Spring 框架的支持。

    2024年02月14日
    浏览(46)
  • 【Spring Boot】单元测试

    单元测试在日常项目开发中必不可少,Spring Boot提供了完善的单元测试框架和工具用于测试开发的应用。接下来介绍Spring Boot为单元测试提供了哪些支持,以及如何在Spring Boot项目中进行单元测试。 单元测试主要用于测试单个代码组件,以确保代码按预期方式工作。目前流行的

    2024年02月16日
    浏览(46)
  • Spring Boot单元测试

    ❤️❤️❤️SSM专栏更新中,各位大佬觉得写得不错,支持一下,感谢了!❤️❤️❤️ Spring + Spring MVC + MyBatis_冷兮雪的博客-CSDN博客 Spring Boot 中进行单元测试是一个常见的做法,可以帮助你验证应用程序的各个组件是否按预期工作。所以我们有必要去学习一番! 单元测试

    2024年02月13日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包