SpringBoot单元测试--Mockito+Junit5框架使用

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

前言

作为程序员为了提前发现代码bug,优化代码; 通常我们写完某个功能模块代码后都需要写单元测试对代码块进行测试(特别是敏捷开发中);Java项目最常用的单元测试框架即为Junit(目前最新版本为Junit5),SpringBoot本身也整合了该框架。在写单元测试时代码块中的调到第三方接口方法或涉及数据库操作的接口方法一般都需要mock掉(测试中叫打测试桩)。目前在 Java 中主流的 Mock 测试框架有 Mockito、JMock、EasyMock,Mockito 框架是SpringBoot 目前内建的 框架。本文主要介绍Junit5+Mockito在SpringBoot项目写单元测试的使用。

maven依赖

Mockito,Junit在SpringBoot 内部已依赖只需引入spring-boot-starter-test即可。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
      <exclusion>
        <groupId>org.junit.vintage</groupId>
        <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <scope>test</scope>
</dependency>

Junit5基本使用

基本注解:

类注解:

@TestInstance(Lifecycle.PER_CLASS)注解

如果您希望JUnit Jupiter在同一个测试实例上执行所有测试方法,只需使用@TestInstance(Lifecycle.PER_CLASS)注释您的测试类。使用此模式时,每个测试类将创建一个新的测试实例。如果没使用@TestInstance(Lifecycle.PER_CLASS)注解,使用@BeforeAll和@AfterAll注解必须在static静态方法上使用。

  • TestInstance.Lifecycle.PER_CLASS:每个测试类将创建一个新的测试实例。
  • TestInstance.Lifecycle.PER_METHOD:将为每种测试方法,测试工厂方法或测试模板方法创建一个新的测试实例。此模式类似于JUnit版本1至4中的行为。
@ExtendWith(MockitoExtension.class)注解

用在springboot项目中,涉及spring的单元测试需要使用@ExtendWith(SpringExtension.class)注解,可以mock spring bean。不涉及spring时使用@ExtendWith(MockitoExtension.class)。

@ExtendWith(SpringExtension.class)注解在Spring boot 2.1.x需要配合@SpringBootTest 使用,Spring boot 2.1.x之后可以不使用@ExtendWith(SpringExtension.class)注解

参考文档:Java – 理解 @ExtendWith(SpringExtension.class) 和 @ExtendWith(MockitoExtension.class)之间的差别

@SpringBootTest(classes = Application.class)注解

classes = ApplicationStarter.class指向SpringBoot启动类,启动spring容器。

在不同的Spring Boot版本中@ExtendWith的使用:

其中在Spring boot 2.1.x之前: 

@SpringBootTest 需要配合@ExtendWith(SpringExtension.class)才能正常工作的。

而在Spring boot 2.1.x之后: 

@SpringBootTest 已经组合了@ExtendWith(SpringExtension.class),因此,无需在进行该注解的使用了,进一步简化。如下图@SpringBootTest注解中已包含@ExtendWith(SpringExtension.class):

 

@extendwith(mockitoextension.class),测试开发,软件开发,单元测试,spring boot,junit,spring,java

方法注解:

基本的注解都是方法上的注解,意思就是只在测试方法上进行添加,对应注解有以下几种:

注解 说明
@Test 测试方法的入口;可单独运行
@BeforeEach 每个测试方法前运行;不可以单独运行该方法
@AfterEach 每个测试方法后运行;不可以单独运行该方法
@BeforeAll  在类中所有方法前运行;static修饰;不可单独运行该方法
@AfterAll 在类中所有方法后运行;static修饰;不可单独运行该方法

代码示例:

import org.mockito.InjectMocks;

@TestInstance(Lifecycle.PER_CLASS)
@SpringBootTest(classes = ApplicationStarter.class)
public class DemoServiceImplTest {

    @InjectMocks
    private DemoService demoService =new DemoServiceImpl();

    @BeforeAll
    void beforeAllInit() {
        System.out.println("running before all");
    }

    @AfterAll
    void afterAllCleanUp() {
        System.out.println("running after all");
    }

    @BeforeEach
    void init() {
        System.out.println("running before each...");
    }

    @AfterEach
    void cleanUp() {
        System.out.println("running after each...");
    }

    @Test
    void testSum() {
        assertEquals(2, demoService.addtwoNumbers(1, 1));
    }

}

断言校验:

Assertions.assertEquals()值比较校验:

assertEquals(expected, actual,message)里面最少是2个参数,一个自己的期望值「expected」,一个程序的实际值「 actual」。如果想要断言失败的情况下显示自定义的说明,则加上第3个参数,即断言失败说明「message」。

Assertions.assertThrows()异常捕获校验:

assertThrows(Class<T> expectedType, Executable executable, String message)

去判断代码抛出的异常是业务代码自定义的异常不,对应的期望值变成了异常类型「Class<T>」的期望值,实际的值也是抛出异常的实际值「Executable」,同样如果想要断言失败的情况下显示自定义的说明,则加上第3个参数,即断言失败说明「message」。

代码示例:

import org.mockito.InjectMocks;

@TestInstance(Lifecycle.PER_CLASS)
@SpringBootTest(classes = ApplicationStarter.class)
public class DemoServiceImplTest {

    @InjectMocks
    private DemoService demoService =new DemoServiceImpl();

    
    @Test
    public void testSum() {
        //Assertions.assertThrows()
        Exception ex = Assertions.assertThrows(Exception.class, () ->demoService.addtwoNumbers(1, 1))
        //Assertions.assertEquals()
        Assertions.assertEquals(ex.getMessage(),"test");
    }

}

更多详细信息参考文档:test-instance-lifecycle

Mockito使用

在测试代码块中经常会调到第三方接口方法(比如第三方SDK接口方法或远程RPC接口),涉及数据库操作的接口方法(数据库增删改查接口)。这些方法需要其他环境服务支持,链接远程数据库,我们只需测试自己编写的单元代码块是否有问题,不想真实调用这些方法。要解决这个问题,可以把这些方法都mock(模拟)掉。Mockito框架提供很好的支持。

常用注解

  • @Mock:创建一个Mock,用于替换被测试类中的引用的bean或第三方类。
  • @InjectMocks:用于创建一个被测试类的实例,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。用于被测试类(如service层的ServiceImpl)
  • @Mockbean:将Mock对象添加到Spring上下文中。Mock将替换Spring上下文中任何相同类型的现有bean,如果没有定义相同类型的bean,将添加一个新的bean。如果需要使用Mockbean注解,需要使用SpringRunner(Junit5 中是@ExtendWith(SpringExtension.class)
    )

@Autowird 等方式完成自动注入。在单元测试中,没有启动 spring 框架,此时就需要通过 @ InjectMocks完成依赖注入。@InjectMocks会将带有@Spy 和@Mock 注解的对象尝试注入到被 测试的目标类中。如下代码示例:

代码示例:



@Component("mock")
public class MockRepository {


    public MockData mock(String userName) {
        return new MockData(userName);
        
    }

}
import org.mockito.InjectMocks;

@Service
public class DemoServiceImpl {


    @Autowired
    private UserRepository userRepository;


    @Autowired
    private ApplicationContext applicationContext;
    

    @Override
    public Result getUserInfo(String id) {
        User user=userRepository.findUserById(id);
        MockRepository mockRepository=applicationContext.getBean("mock");
        MockData data=mockRepository.mock(user.getUserName());
        return new Result("1000","success",data);
        
    }

}
import org.mockito.InjectMocks;

@TestInstance(Lifecycle.PER_CLASS)
@SpringBootTest(classes = ApplicationStarter.class)
public class DemoServiceImplTest {

    @InjectMocks
    private DemoService demoService =new DemoServiceImpl();

    @Mock
    private UserRepository userRepository;

    @MockBean
    private MockRepository mockRepository;

    @Autowired
    private ApplicationContext applicationContext;
    
    @Test
    public void testSum() {
        //Assertions.assertThrows()
        Result res =demoService.getUserInfo("test");
        //Assertions.assertEquals()
        Assertions.assertEquals(res.getCode(),"1000");
    }

}

Mock方法:

1.when(...) thenReturn(...)会调用真实的方法,如果你不想调用真实的方法而是想要mock的话,就不要使用这个方法。


import org.mockito.InjectMocks;

@TestInstance(Lifecycle.PER_CLASS)
@SpringBootTest(classes = ApplicationStarter.class)
public class DemoServiceImplTest {

    @InjectMocks
    private DemoService demoService =new DemoServiceImpl();

    @Mock
    private UserRepository userRepository;

    @MockBean
    private MockRepository mockRepository;

    @Autowired
    private ApplicationContext applicationContext;
    
    @Test
    public void testSum() {
        // when(..).thenReturn(..)
        Mockito.when(userRepository.findUserById(Mockito.anyString())).thenReturn(new User());    
        Result res =demoService.getUserInfo("test");
        //Assertions.assertEquals()
        Assertions.assertEquals(res.getCode(),"1000");
    }

}


2.doReturn(...) when(...) 跟when(...) thenReturn(...)一样都是mock方法,但不会调用真实方法。

import org.mockito.InjectMocks;

@TestInstance(Lifecycle.PER_CLASS)
@SpringBootTest(classes = ApplicationStarter.class)
public class DemoServiceImplTest {

    @InjectMocks
    private DemoService demoService =new DemoServiceImpl();

    @Mock
    private UserRepository userRepository;

    @MockBean
    private MockRepository mockRepository;

    @Autowired
    private ApplicationContext applicationContext;
    
    @Test
    public void testSum() {
        // doReturn(new User()).when(userRepository)    
        Mockito.doReturn(new User()).when(userRepository).findUserById(Mockito.anyString()))
        Result res =demoService.getUserInfo("test");
        //Assertions.assertEquals()
        Assertions.assertEquals(res.getCode(),"1000");
    }

}

3.doAnswer…when 当模拟对象调用它的方法,需要执行一些操作(其实就是需要执行一个代码块)才能得到返回值时,则需要使用doAnswer来构造产生这个模拟的返回值。例如:当模拟对象调用某个方法的返回值是个复合值(bean)时,就需要用doAnswer来构造该返回值。文章来源地址https://www.toymoban.com/news/detail-781927.html

@InjectMocks
private DemoService demoService =new DemoServiceImpl();

@Mock
private StockDao stockDao;
...

@Test
public void stockTest() {
    
        doAnswer(new Answer<StockModel>) {
            @Override
            public StockModel answer(InvocationOnMock invocation) throws Throwable {
                StockModel  stock = new StockModel ();
                stock.setFundFamilyName("fundFamily01");
                return stock;
            }
        }).when(stockDao).lookup("testStock");
        Result res=demoService.stock("testStock");
        Assertions.assertEquals(res.getStock(),"test");
    
}

到了这里,关于SpringBoot单元测试--Mockito+Junit5框架使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 深度揭秘JUnit5与Mockito的单元测试神秘面纱

    在今天的学习中,我们将深入研究 JUnit 和Mockito,这是 Java 开发中最强大的 单元测试 工具之一。通过学习如何编写清晰、高效的单元测试,我们将揭开单元测试的神秘面纱,助力你在项目中写出更健壮的代码。 提示: 今天的代码是在第九天代码的基础上进行开发,我们将为

    2024年02月02日
    浏览(58)
  • 【单元测试】如何使用 JUnit5 框架?

      Junit5是一个用于在Java平台上进行单元测试的框架。JUnit 5 框架主要由三部分组成:JUnit Platform、JUnit Jupiter 和 JUnit Vintage。 JUnit Platform:定义了测试引擎的 API,是 JVM 上用于启动测试框架的基础服务,支持通过 IDE、构建工具、命令行等方式运行单元测试。 JUnit Jupiter:包含

    2024年04月10日
    浏览(46)
  • 单元测试框架——Junit5

    Junit是一个开源的用于Java语言的单元测试框架,也是Java方向使用最广泛的单元测试框架。 在pom.xml中引入Junit5相关依赖 @Test :表示一个方法/用例 BeforeEach :表示被注解的方法在其它所有方法执行前都要执行一遍,也就是说其它方法有3个它就要执行3遍 @BeforeAll :表示被注解的

    2024年02月11日
    浏览(44)
  • Junit5单元测试框架详解

    前面我们学习了Selenium自动化测试框架,但是有的时候测试用例会很多,我们需要一个工具来管理这些测试用例,而Junit就是一个很好的管理工具,简单点来说,Junit就是一个针对Java的单元测试框架; 目录 一. 关于Junit5 二. Junit使用 2.1 添加Maven依赖 2.2 注解 2.3 断言 2.4 套件

    2024年02月06日
    浏览(49)
  • SpringBoot2---单元测试(Junit5)(1)

    org.junit.vintage junit-vintage-engine test org.hamcrest hamcrest-core org.springframework.boot spring-boot-starter-test test 现在版本: @SpringBootTest class Boot05WebAdminApplicationTests { @Test void contextLoads() { } } 以前: @SpringBootTest + @RunWith(SpringRunner.class) SpringBoot整合Junit以后。 编写测试方法:@Test标注(注意需要

    2024年04月29日
    浏览(43)
  • 13.Springboot整合junit5单元测试与生成单元测试覆盖率

    现在基本大公司都要求单元测试了,保证我们代码得质量,而我司更是要求覆盖率要达到60%以上,所以搞一下。 这里有两个方法: 1.使用maven自带得test,idea右侧maven模块执行项目下得test 2.使用cmd命令,在你的项目pom文件所在目录 ,打开cmd,执行如下: 结果如下:打开site文

    2024年02月16日
    浏览(39)
  • 【SpringBoot】mockito+junit 单元测试

    CommonServiceImpl 在方法 getSourceCodeMap() 调用了 ServiceA 的方法 list(QueryBO queryBo) 。 org.mockito.exceptions.base.MockitoException: No tests found in ClientSyncServiceImplTest Is the method annotated with @Test? Is the method public? 解决方案: Test引入 org.junit.Test 不要引入 org.junit.jupiter.api.Test 例如获取当前用户 moc

    2024年02月09日
    浏览(44)
  • 静态方法 单元测试 springboot+mokito+junit5

    CodecUtils的方法是静态类,使用@InjectMocks不能有用,因为这个注解只能用于非静态的对象。 想要为静态方法写单元测试,可以使用Mockito.mockStatic(Class classToMock)方法,它可以返回一个MockedStatic对象,用于模拟静态方法的调用。 1.导入依赖 2.单元测试 可以参考如下地址,了解如何

    2024年04月25日
    浏览(47)
  • 单元测试junit(原始版本、Spring Boot各版本、junit5)使用介绍

    🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢迎点赞 👍 收藏 ⭐留言 📝 🍓 更多文章请点击 单元测试 junit各版本 使用介绍 官

    2023年04月16日
    浏览(44)
  • 在 Java 中使用JUnit5进行单元测试和自动化测试

    单元测试和自动化测试是现代软件开发过程中必不可少的环节,可以提高代码质量和开发效率。JUnit5是Java中流行的单元测试框架,本文将介绍如何在Java中使用JUnit5进行单元测试和自动化测试。 2.1 单元测试的基本概念和原理 单元测试是一种测试方法,用于对软件系统中的最

    2024年02月03日
    浏览(45)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包