JUNIT5+Mockito单元测试

1、前言

之前写过一篇使用testMe自动生成单元测试用例,使用的是junit4来编写的单元测试用例,目前很多新项目都已经使用JDK11+以及SpringBoot3+。本次基于junit5+Mockito来编写单元测试。

2、Maven依赖

2.1 JDK21+SpringBoot版本基于3.1.0

SpringBoot依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.objenesis</groupId>
                    <artifactId>objenesis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

mockito依赖

<!--junit5单元测试-->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>5.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>5.3.1</version>
        </dependency>

lombok依赖

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>edge-SNAPSHOT</version>
        </dependency>

2.2 JDK17+SpringBoot版本基于2.2.5.RELEASE

SpringBoot依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
    </parent>

Junit依赖

<dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
        </dependency>

mockito依赖

<dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>5.2.0</version>
            <exclusions>
                <exclusion>
                    <groupId>net.bytebuddy</groupId>
                    <artifactId>byte-buddy</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>net.bytebuddy</groupId>
                    <artifactId>byte-buddy-agent</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.14.1</version>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.14.1</version>
        </dependency>

lombok依赖

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>

3、业务代码

package com.summer.toolkit.mock;

import com.alibaba.fastjson.JSON;
import com.summer.toolkit.dto.UserDto;
import com.summer.toolkit.exception.BizException;
import com.summer.toolkit.executor.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;


@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserManager userManager;

    @Override
    public Long createUser(UserDto userDto) {
        log.info("创建用户入参:{}", JSON.toJSONString(userDto));
        String name = userDto.getUsername();
        if (StringUtils.isBlank(name)) {
            log.error("用户名称不能为空");
            throw new BizException("用户名称不能为空");
        }

        Long id = userManager.createUser(userDto);
        log.info("创建用户出参:{}", id);
        return id;
    }

    @Override
    public Boolean updateUser(UserDto userDto) {
        log.info("更新用户入参:{}", JSON.toJSONString(userDto));
        Long id = userDto.getId();
        String name = userDto.getUsername();
        if (Objects.isNull(id)) {
            log.error("用户主键不能为空");
            throw new BizException("用户主键不能为空");
        }
        if (StringUtils.isBlank(name)) {
            log.error("用户名称不能为空");
            throw new BizException("用户名称不能为空");
        }

        UserDto user = userManager.getUser(userDto);
        if (Objects.isNull(user)) {
            log.error("用户不存在");
            throw new BizException("用户不存在");
        }
        Boolean result = userManager.updateUser(userDto);
        log.info("更新用户出参:{}", result);
        return result;
    }

    @Override
    public UserDto getUser(UserDto userDto) {
        log.info("获取用户入参:{}", JSON.toJSONString(userDto));
        Long id = userDto.getId();
        if (Objects.isNull(id)) {
            log.error("用户主键不能为空");
            throw new BizException("用户主键不能为空");
        }
        UserDto user = userManager.getUser(userDto);
        log.info("获取用户出参:{}", user);
        return user;
    }

    @Override
    public Boolean batchCreateUser(List<UserDto> list) {
        log.info("批量创建用户入参:{}", JSON.toJSONString(list));
        if (CollectionUtils.isEmpty(list)) {
            log.error("入参集合不能为空");
            throw new BizException("入参集合不能为空");
        }

        int size = 10;
        long keepAliveTime = 60;
        long start = System.currentTimeMillis();
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10000);
        ThreadFactory threadFactory = new DefaultThreadFactory("executor");
        ExecutorService executorService
                = new ThreadPoolExecutor(size, size, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);

        List<CompletableFuture<Boolean>> futureList = new ArrayList<>();
        for (UserDto userDto : list) {
            CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
                log.info("当前线程名称:{}", Thread.currentThread());
                try {
                    Long id = userManager.createUser(userDto);
                    TimeUnit.SECONDS.sleep(3L);
                    log.info("线程:{} id={} done", Thread.currentThread(), id);
                    return Boolean.TRUE;
                } catch (InterruptedException e) {
                    log.error("创建用户异常:{}", e.getMessage(), e);
                    return Boolean.FALSE;
                }
            }, executorService);
            futureList.add(future);
        }

        Boolean result = Boolean.TRUE;
        try {
            CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).get(10, TimeUnit.SECONDS);
            for (CompletableFuture<Boolean> future : futureList) {
                Boolean back = future.get();
                if (Boolean.FALSE.equals(back)) {
                    result = Boolean.FALSE;
                }
            }
        } catch (Exception e) {
            log.error("创建用户异常:{}", e.getMessage(), e);
            result = Boolean.FALSE;
        }

        long end = System.currentTimeMillis();
        log.info("批量创建用户耗时:{}", (end - start));

        log.info("批量创建用户出参:{}", result);
        return result;
    }

}

4、单元测试

package com.summer.toolkit.service;

import com.summer.toolkit.dto.UserDto;
import com.summer.toolkit.exception.BizException;
import com.summer.toolkit.mock.UserManager;
import com.summer.toolkit.mock.UserServiceImpl;
import com.summer.toolkit.util.FileUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;


@ExtendWith(MockitoExtension.class)
@EnabledIfEnvironmentVariable(named = "DEBUG", matches = "true")
public class UserServiceTest {

    @Mock
    private Logger log;
    @Mock
    private UserManager userManager;

    @InjectMocks
    private UserServiceImpl userService;

    @Test
    public void testCreateUser() {
        // 模拟依赖方法
        Mockito.when(userManager.createUser(any())).thenReturn(Long.valueOf(1));

        // 调用被测方法
        UserDto userDto = this.buildUserDto();
        Long result = userService.createUser(userDto);

        // 验证方法结果
        Long expect = 1L;
        Assertions.assertEquals(expect, result);
        // 验证方法是否被调用
        Mockito.verify(userManager).createUser(userDto);

        // 验证依赖对象只有这一个
        Mockito.verifyNoMoreInteractions(userManager);
    }

    @Test
    public void testUpdateUser() {
        // 模拟依赖方法
        Mockito.when(userManager.updateUser(any())).thenReturn(Boolean.TRUE);
        Mockito.when(userManager.getUser(any())).thenReturn(new UserDto());

        // 调用被测方法
        UserDto userDto = this.buildUserDto();
        userDto.setId(1L);
        Boolean result = userService.updateUser(userDto);

        // 验证方法结果
        Assertions.assertEquals(Boolean.TRUE, result);
        // 验证方法是否被调用
        Mockito.verify(userManager).getUser(any());
        Mockito.verify(userManager).updateUser(any());
    }

    @Test
    public void testGetUser() {
        // 模拟依赖方法
        Mockito.when(userManager.getUser(any())).thenReturn(new UserDto());

        // 调用被测方法
        UserDto userDto = this.buildUserDto();
        userDto.setId(1L);
        UserDto result = userService.getUser(userDto);

        // 验证方法结果
        Assertions.assertNotNull(result);
        // 验证方法是否被调用
        Mockito.verify(userManager).getUser(userDto);
    }

    @Test
    public void testBatchCreateUser() {
        // 模拟依赖方法,指定单个异常类型
        Mockito.when(userManager.createUser(any())).thenThrow(BizException.class);

        // 调用被测方法
        List<UserDto> param = new ArrayList<>();
        UserDto userDto = this.buildUserDto();
        param.add(userDto);
        Boolean result = userService.batchCreateUser(param);

        // 验证方法结果
        Assertions.assertEquals(Boolean.FALSE, result);
        // 验证方法是否被调用,默认一次
        Mockito.verify(userManager).createUser(userDto);
        // 验证方法是否被调用了1次
        Mockito.verify(userManager, Mockito.times(1)).createUser(any());
    }

    @Test
    public void testBatchCreateUserTimes() {
        // 模拟依赖方法,指定单个异常类型
        Mockito.when(userManager.createUser(any())).thenReturn(1L);

        // 调用被测方法
        List<UserDto> param = new ArrayList<>();
        UserDto userDto = this.buildUserDto();
        param.add(userDto);
        param.add(userDto);
        param.add(userDto);
        Boolean result = userService.batchCreateUser(param);

        // 验证方法结果
        Assertions.assertEquals(Boolean.TRUE, result);
        // 验证方法是否被调用了3次
        Mockito.verify(userManager, Mockito.times(3)).createUser(any());
    }

    @Test
    public void testFileUtils() {
        // 构建对象
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");

        // 模拟对应的类
        // JDK11及以上版本中,try块中的变量可以在外部声明
        MockedStatic<FileUtils> mocked = Mockito.mockStatic(FileUtils.class);
        try (mocked) {
            // 模拟依赖静态方法
            mocked.when(() -> FileUtils.readFileAllLines(anyString())).thenReturn(list);

            // 调用被测方法
            List<String> lines = FileUtils.readFileAllLines(anyString());

            // 验证方法结果
            Assertions.assertEquals(list.size(), lines.size());
        } catch (Exception e) {
            log.error("模拟静态方法异常:{}", e.getMessage(), e);
        }
    }

    /**
     * 构建用户数据传输对象
     *
     * @return UserDto 返回构建好的用户数据传输对象
     */
    private UserDto buildUserDto() {
        UserDto userDto = new UserDto();
        userDto.setUsername("小明");
        userDto.setBirthday(new Date());
        userDto.setAddress("北京市大兴区亦庄经济开发区");
        userDto.setComment("加麻加辣");
        userDto.setGender(1);
        return userDto;
    }

}


相关推荐

  1. JUNIT5+Mockito单元测试

    2024-03-14 05:40:04       22 阅读
  2. JUnit 5Mockito单元测试

    2024-03-14 05:40:04       39 阅读
  3. Mockito+junit5搞定单元测试

    2024-03-14 05:40:04       37 阅读
  4. JUnit 5 单元测试框架

    2024-03-14 05:40:04       37 阅读
  5. 【Springboot】单元测试Junit5应用

    2024-03-14 05:40:04       29 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-14 05:40:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-14 05:40:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-14 05:40:04       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-14 05:40:04       20 阅读

热门阅读

  1. LeetCode_27_简单_移除元素

    2024-03-14 05:40:04       18 阅读
  2. 【Educoder数据挖掘实训】相似度与相异度

    2024-03-14 05:40:04       16 阅读
  3. 在阿里云上配置开放端口的详细教程

    2024-03-14 05:40:04       22 阅读
  4. 基于qt和css的MP3音乐播放器引擎开发

    2024-03-14 05:40:04       18 阅读
  5. 分布式ID(8):分布式ID生成方法

    2024-03-14 05:40:04       25 阅读
  6. .NET中使用Redis大全

    2024-03-14 05:40:04       21 阅读
  7. volatile关键字用处和场景?

    2024-03-14 05:40:04       20 阅读
  8. 17 OpenCv Canny算子

    2024-03-14 05:40:04       17 阅读
  9. HiveQL详解

    2024-03-14 05:40:04       19 阅读
  10. 视频测试示例

    2024-03-14 05:40:04       14 阅读
  11. 什么是池架构?

    2024-03-14 05:40:04       17 阅读
  12. OpenCV加载视频

    2024-03-14 05:40:04       20 阅读
  13. AI大语言模型GPT & R 生态环境领域数据统计分析

    2024-03-14 05:40:04       21 阅读
  14. 软考笔记--层次式架构之数据访问层设计

    2024-03-14 05:40:04       17 阅读
  15. 浅谈Spring Cloud Ribbon原理及其使用方法

    2024-03-14 05:40:04       19 阅读
  16. 在组件上使用v-model

    2024-03-14 05:40:04       18 阅读