当前位置:首页 > 科技  > 软件

三个要点,掌握Spring Boot单元测试

来源: 责编: 时间:2023-09-28 10:06:01 388观看
导读单元测试是软件开发中不可或缺的重要环节,它用于验证软件中最小可测试单元的准确性。结合运用Spring Boot、JUnit、Mockito和分层架构,开发人员可以更便捷地编写可靠、可测试且高质量的单元测试代码,确保软件的正确性和

单元测试是软件开发中不可或缺的重要环节,它用于验证软件中最小可测试单元的准确性。结合运用Spring Boot、JUnit、Mockito和分层架构,开发人员可以更便捷地编写可靠、可测试且高质量的单元测试代码,确保软件的正确性和质量。Lpe28资讯网——每日最新资讯28at.com

Lpe28资讯网——每日最新资讯28at.com

一、介绍

本文将从与单元测试相关的技术主题开始,在技术部分之后,介绍使用Spring Boot、JUnit和Mockito进行单元测试的实践。Lpe28资讯网——每日最新资讯28at.com

二、测试的关键要素

1.单元

单元测试中的单元一词指的是软件中可以单独测试和处理的最小功能部分,通常是指函数、方法、类或模块等独立的代码片段。Lpe28资讯网——每日最新资讯28at.com

2.用例

用例描述了系统使用特定功能或特性的方式,用于理解、设计和测试软件系统的需求。通常包括用户如何与系统进行交互、对系统的期望以及应该实现的结果等详细信息。Lpe28资讯网——每日最新资讯28at.com

3.边界情况

边界情况指的是软件必须处理的特定场景,这些场景包括意外或边界条件,与典型情况有所不同或被认为是罕见的情况。边界情况可以包括意外用户登录、测试限制、异常输入或其他可能导致系统错误或异常行为的情况。在测试过程中,考虑和测试边界情况是非常重要的,因为它们可以帮助开发人员发现潜在的问题并确保系统的鲁棒性和稳定性。Lpe28资讯网——每日最新资讯28at.com

三、单元测试

单元测试涵盖了我们可以考虑并编写的所有可能性。每个单元必须至少有一个测试方法。测试不是为一个方法编写的,而是为一个单元编写的。Lpe28资讯网——每日最新资讯28at.com

可以按照以下顺序编写单元测试:正常路径/用例、边界情况和异常情况。Lpe28资讯网——每日最新资讯28at.com

这些步骤是必不可少的,这样做可以确保单元以正确的方式处理输入,并生成预期的输出,展现出预期的行为。单元测试是及早发现风险和修复错误的最佳方式。通过单元测试,我们可以预防潜在的意外情况,应对生产代码的变更,确保生产代码能够处理各种情况。简而言之,单元测试确保了生产代码的安全性。Lpe28资讯网——每日最新资讯28at.com

关于单元测试的另一个重要事项是要测试业务逻辑,不是在单元测试中测试基础设施代码,基础设施代码可以在集成测试中进行测试。可以考虑使用一些架构模式(如洋葱架构、六边形架构等)来将业务逻辑与基础设施代码分离。Lpe28资讯网——每日最新资讯28at.com

单元测试的另一个优点是速度快,因为它不需要依赖 Spring ApplicationContext。由于上下文的原因,与单元测试相比,同一测试金字塔中的集成测试速度要慢得多。Lpe28资讯网——每日最新资讯28at.com

1.开始编码

在分层架构项目中,业务代码主要位于服务层。这意味着服务层具有单元,需要进行测试。让我们聚焦于最关键的部分。Lpe28资讯网——每日最新资讯28at.com

以下是一段示例代码:Lpe28资讯网——每日最新资讯28at.com

  @Override    public String saveUser(User user) {        validateUser(user);        try {            User savedUser = userRepository.save(user);            return savedUser.getEmail();        } catch (Exception exception) {            throw new IllegalArgumentException(E_GENERAL_SYSTEM);        }    }    private void validateUser(User user) {        if (Objects.isNull(user.getEmail())) {            throw new IllegalArgumentException(E_USER_EMAIL_MUST_NOT_BE_NULL);        }        if (findByEmail(user.getEmail()).isPresent()) {            throw new IllegalArgumentException(E_USER_ALREADY_REGISTERED);        }    }    @Override    public Optional<User> findByEmail(String email) {        return userRepository.findByEmail(email);    }

上述代码中有两个公共方法和一个私有方法,私有方法可以被视为公共方法的一部分。此外,由于代码的复杂性和功能需求,还存在许多可能的场景需要编写多个测试用例来覆盖各种情况,以确保代码的正确性。Lpe28资讯网——每日最新资讯28at.com

2.注解

@ExtendWith用于将Mockito库集成到JUnit测试中。@Test 标记一个方法,使其成为一个测试方法,测试方法包含指定的测试用例,并由 JUnit 自动运行。Lpe28资讯网——每日最新资讯28at.com

在测试过程中,需要模拟正在测试的类的依赖项。之前提到的原因是,由于 Spring ApplicationContext 不会启动,我们无法将依赖项注入到上下文中。@Mock 用于创建一个模拟的依赖项,而 @InjectMocks 则用于将这些模拟的依赖项注入到被测试类中。Lpe28资讯网——每日最新资讯28at.com

@BeforeEach和@AfterEach可用于在每个方法运行之前和之后执行相应的操作。Lpe28资讯网——每日最新资讯28at.com

@ParameterizedTest 用于使用不同的参数值运行重复的测试用例。通过使用 @ValueSource,可以为方法提供不同的参数值,以便进行多次测试。Lpe28资讯网——每日最新资讯28at.com

3.测试方法的三个主要阶段

  • Given: 准备测试用例所需的对象
  • When: 执行必要的操作以运行测试场景
  • Then: 检查或验证预期结果

doReturn/when 用于确定在给定指定参数时方法的行为方式。但是,由于依赖项是 @Mock,并不会真正执行。Lpe28资讯网——每日最新资讯28at.com

verify 用于检查被测试代码是否按照预期行为执行。如果要测试的方法是 public void 类型,可以使用 verify 进行验证。Lpe28资讯网——每日最新资讯28at.com

断言用于验证预期结果。Lpe28资讯网——每日最新资讯28at.com

 @ExtendWith(MockitoExtension.class)class UserServiceImplTest {    @InjectMocks    private UserServiceImpl userService;    @Mock    private UserRepository userRepository;    private User user;    public static final String MOCK_EMAIL = "mert@bahardogan.com";    @BeforeEach    void setUp() {        user = new User();        System.out.println("init");    }    @AfterEach    void teardown() {        System.out.println("teardown");    }    @ParameterizedTest    @ValueSource(strings = {"mert@bahardogan.com", "info@gmail.com"})    @DisplayName("Happy Path: save user use cases")    void givenCorrectUser_whenSaveUser_thenReturnUserEmail(String email) {        // given        user.setUserName("mertbahardogan").setEmail(email).setPassword("pass");        User savedUser = new User().setEmail(email);        doReturn(savedUser).when(userRepository).save(any());        // when        String savedUserEmail = userService.saveUser(user);        // then        verify(userRepository,times(1)).findByEmail(anyString());        verify(userRepository,times(1)).save(any());        assertEquals(email, savedUserEmail);    }    @Test    @DisplayName("Exception Test: user email must not be null case")    void givenNullUserEmail_whenSaveUser_thenThrowsEmailMustNotNullEx() {        // when        Exception exception = assertThrows(IllegalArgumentException.class, () -> userService.saveUser(user));        // then        assertNotNull(exception);        assertEquals(E_USER_EMAIL_MUST_NOT_BE_NULL, exception.getMessage());    }    @Test    @DisplayName("Exception Test: user is already registered case")    void givenRegisteredUser_whenSaveUser_thenThrowsUserAlreadyRegisteredEx() {        // given        user.setEmail(MOCK_EMAIL);        Optional<User> savedUser = Optional.of(new User().setEmail(MOCK_EMAIL));        doReturn(savedUser).when(userRepository).findByEmail(anyString());        // when        Exception exception = assertThrows(IllegalArgumentException.class, () -> userService.saveUser(user));        // then        assertNotNull(exception);        assertEquals(E_USER_ALREADY_REGISTERED, exception.getMessage());    }    @Test    @DisplayName("Exception Test: catch case")    void givenIncorrectDependencies_whenSaveUser_thenThrowsGeneralSystemEx() {        // given        user.setEmail(MOCK_EMAIL);        // when        Exception exception = assertThrows(IllegalArgumentException.class, () -> userService.saveUser(user));        // then        assertNotNull(exception);        assertEquals(E_GENERAL_SYSTEM, exception.getMessage());    }    @Test    @DisplayName("Happy Path: find user by email")    void givenCorrectUser_whenFindByEmail_thenReturnUserEmail() {        // given        Optional<User> savedUser = Optional.of(new User().setEmail(MOCK_EMAIL));        doReturn(savedUser).when(userRepository).findByEmail(anyString());        // when        Optional<User> user = userService.findByEmail(MOCK_EMAIL);        // then        verify(userRepository,times(1)).findByEmail(anyString());        assertEquals(savedUser, user);    }}

UserServiceImpl测试类运行时长为1秒693毫秒。Lpe28资讯网——每日最新资讯28at.com

Lpe28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-11820-0.html三个要点,掌握Spring Boot单元测试

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: DevOps优秀实践之用户与权限

下一篇: C 语言程序是如何在计算机内部工作?

标签:
  • 热门焦点
  • MIX Fold3包装盒泄露 新机本月登场

    小米的全新折叠屏旗舰MIX Fold3将于本月发布,近日该机的真机包装盒在网上泄露。从图上来看,新的MIX Fold3包装盒在外观设计方面延续了之前的方案,变化不大,这也是目前小米旗舰
  • 一加Ace2 Pro真机揭晓 钛空灰配色质感拉满

    终于,在经过了几波预热之后,一加Ace2 Pro的外观真机图在网上出现了。还是博主数码闲聊站曝光的,这次的外观设计还是延续了一加11的方案,只是细节上有了调整,例如新加入了钛空灰
  • 三言两语说透设计模式的艺术-单例模式

    写在前面单例模式是一种常用的软件设计模式,它所创建的对象只有一个实例,且该实例易于被外界访问。单例对象由于只有一个实例,所以它可以方便地被系统中的其他对象共享,从而减少
  • 微软邀请 Microsoft 365 商业用户,测试视频编辑器 Clipchamp

    8 月 1 日消息,微软近日宣布即将面向 Microsoft 365 商业用户,开放 Clipchamp 应用,邀请用户通过该应用来编辑视频。微软于 2021 年收购 Clipchamp,随后开始逐步整合到 Microsof
  • 使用AIGC工具提升安全工作效率

    在日常工作中,安全人员可能会涉及各种各样的安全任务,包括但不限于:开发某些安全工具的插件,满足自己特定的安全需求;自定义github搜索工具,快速查找所需的安全资料、漏洞poc、exp
  • 一条抖音4亿人围观 ! 这家MCN比无忧传媒还野

    作者:Hiu 来源:互联网品牌官01 擦边少女空降热搜,幕后推手曝光被网友誉为&ldquo;纯欲天花板&rdquo;的女网红井川里予,近期因为一组哥特风照片登上热搜,引发了一场互联网世界关于
  • 华为将推出盘古数字人大模型 可帮助用户12小时完成数字人生成

    在今日举行的2023年华为云数字文娱AI创新峰会上,华为云全球Marketing与销售服务总裁石冀琳表示,华为云将在后续推出盘古数字人大模型,可帮助用户12小
  • 华为Mate60标准版细节曝光:经典星环相机模组回归

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
  • 2022爆款:ROG魔霸6 冰川散热系统持续护航

    喜逢开学季,各大商家开始推出自己的新产品,进行打折促销活动。对于忠实的端游爱好者来说,能够拥有一款梦寐以求的笔记本电脑是一件十分开心的事。但是现在的
Top