SpringBoot使用junit5+mockito单元测试(不连接中间件)

不启动web容器

springboot版本2.5.4,主要解决在不连接数据库、redis等中间件的情况下进行测试,同时包含mock文件、jedis连接池、成员变量、接口的具体方法

1. 引入jar包

  • 这里使用gradle,不是maven,使用了redis,mybatis-plus
//redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'redis.clients:jedis'

//mybatis-plus
implementation group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.4.3.1'

//test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//mock redis客户端时使用
implementation 'ai.grakn:redis-mock:0.1.6'

2. 被测试类

  • 只为演示单元测试的具体实现,这里省略被测试类之外的其他类的代码
package com.example.handlerinterceptor.sysuser.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.handlerinterceptor.sysuser.dao.SysUserDao;
import com.example.handlerinterceptor.sysuser.entity.SysUser;
import com.example.handlerinterceptor.sysuser.service.SysUserService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;

@Service("sysUserService")
public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUser> implements SysUserService {

    @Value("${fileType}")
    private Set<String> fileType;

    @Resource
    private SysUserDao sysUserDao;

    @Resource
    private JedisConnectionFactory jedisConnectionFactory;

    @Override
    public List<SysUser> getSysUsers(String roleName) {
        // 为了演示mock成员变量,无实际意义
        System.out.println(fileType);
        // 为了演示mock jedis连接池,无实际意义
        Jedis jedis = (Jedis)jedisConnectionFactory.getConnection().getNativeConnection();
        Set<String> keys = jedis.keys("*");
        System.out.println(keys);
        // 为了演示解决mybatis-plus的can not find lambda cache for this entity错误处理方式
        LambdaQueryWrapper<SysUser> queryWrapper =
            new LambdaQueryWrapper<SysUser>().eq(SysUser::getRole, roleName).orderByAsc(SysUser::getUserId);
        List<SysUser> sysUsers = sysUserDao.selectList(queryWrapper);
        return sysUsers;
    }
}

3. 测试类

  • 如果不需要连接中间件,需要使用注解@ExtendWith(MockitoExtension.class),这样就可以mock所有的方法返回值,而不需要直接请求数据库或者第三方接口
package com.example.handlerinterceptor.sysuser.service.impl;

import ai.grakn.redismock.RedisServer;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.example.handlerinterceptor.sysuser.dao.SysUserDao;
import com.example.handlerinterceptor.sysuser.entity.SysUser;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.mock.web.MockMultipartFile;
import redis.clients.jedis.Jedis;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Set;

@ExtendWith(MockitoExtension.class)
class SysUserServiceImplTest {

    @InjectMocks
    private SysUserServiceImpl sysUserService;

    @Mock
    private SysUserDao sysUserDao;

    @Mock
    private JedisConnectionFactory jedisConnectionFactory;

    /**
     * mock redis客户端
     * https://dzone.com/articles/java-redis-mock
     */
    private static RedisServer redisServer = null;

    @BeforeAll
    public static void init() throws IOException {
        redisServer = RedisServer.newRedisServer(6379);
        redisServer.start();
    }

    @AfterAll
    public static void close() {
        if (redisServer != null) {
            redisServer.stop();
        }
    }

    /**
     * 解决mybatis-plus错误:can not find lambda cache for this entity [com.example.handlerinterceptor.sysuser.entity.SysUser]
     * https://github.com/baomidou/mybatis-plus/issues/2569
     */
    @BeforeAll
    static void initiated() {
        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), "SysUser"), SysUser.class);
    }

    /**
     * {@link SysUserServiceImpl#getSysUsers(String)}
     */
    @Test
    void getSysUsers() {
        // mock 接口类,这里没使用
        HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
        HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
        //mock jedis
        Mockito.doReturn(new JedisConnection(mockJedis())).when(this.jedisConnectionFactory).getConnection();
        //mock 成员变量
        mockField(sysUserService, "fileType", Set.of("jpg", "png", "heic"));
        //设置参数
        String roleName = "vip";
        SysUser sysUser = new SysUser();
        sysUser.setUserId(123L);
        //mock 方法返回值
        Mockito.doReturn(List.of(sysUser)).when(this.sysUserDao)
            .selectList(ArgumentMatchers.any(LambdaQueryWrapper.class));
        //执行方法
        List<SysUser> sysUsers = sysUserService.getSysUsers(roleName);
        System.out.println(sysUsers);
        //断言执行结果
        Assertions.assertNotNull(sysUsers);
        // 没有返回值的方法可以使用verify
        Mockito.verify(sysUserDao, Mockito.times(1)).selectList(ArgumentMatchers.any(LambdaQueryWrapper.class));

    }

    /**
     * mock 文件上传
     */
    private MockMultipartFile mockMultipartFile(String fileName, String contentType) {
        byte[] content = new byte[2];
        content[0] = 1;
        content[1] = 2;
        MockMultipartFile mockMultipartFile = new MockMultipartFile("file", fileName, contentType, content);
        return mockMultipartFile;
    }

    /**
     * mock 成员变量
     */
    private void mockField(Object target, String fieldName, Object value) {
        // jdk 反射
        try {
            Field field = target.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(target, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // spring 反射
        // ReflectionTestUtils.setField(target, fieldName, value);
    }

    /**
     * mock jedis
     */
    private Jedis mockJedis() {
        Jedis jedis = new Jedis(redisServer.getHost(), redisServer.getBindPort());
        jedis.set("redis-a", "123");
        jedis.set("redis-b", "456");
        jedis.set("redis-c", "789");
        return jedis;
    }

    /**
     * mock FeignException Request request = Request.create("GET", "", new HashMap<>(), null, null);
     * Mockito.doThrow(FeignException.NotFound.errorStatus("", Response.builder().status(404).request(request).build()))
     */

}

在这里插入图片描述

需要启动web容器进行接口测试的方式

  • controller接口类
package com.example.handlerinterceptor.sysuser.controller;

import com.example.handlerinterceptor.dto.AjaxResult;
import com.example.handlerinterceptor.sysuser.entity.SysUser;
import com.example.handlerinterceptor.sysuser.service.SysUserService;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("sysUser")
public class SysUserController {
    /**
     * 服务对象
     */
    @Resource
    private SysUserService sysUserService;

    @PostMapping("/getSysUsers")
    public AjaxResult<List<SysUser>> getSysUsers(@RequestBody SysUser sysUser) {
        List<SysUser> sysUsers = sysUserService.getSysUsers(sysUser.getRole());
        return AjaxResult.ok(sysUsers);
    }
    
}

1. 方式一,MockMvc

package com.example.handlerinterceptor.sysuser.controller;

import com.alibaba.fastjson.JSONObject;
import com.example.handlerinterceptor.sysuser.entity.SysUser;
import com.example.handlerinterceptor.sysuser.service.SysUserService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import javax.annotation.Resource;
import java.util.List;

import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@AutoConfigureMockMvc
@SpringBootTest
class SysUserControllerTest {

    private static final String GET_SYS_USERS = "/sysUser/getSysUsers";

    @Resource
    private MockMvc mockMvc;

    /**
     * 需要被mock返回结果的bean,用来替代spring容器中真正的bean;如果不需要mock数据,可以不使用这个bean
     */
    @MockBean
    private SysUserService sysUserService;

    /**
     * {@link SysUserController#getSysUsers(SysUser)}
     */
    @Test
    void getSysUsers() throws Exception {
        String userRole = "{\"role\":\"vip\"}";
        SysUser sysUser = new SysUser();
        sysUser.setUserId(123L);
        Mockito.when(sysUserService.getSysUsers(ArgumentMatchers.anyString())).thenReturn(List.of(sysUser));
        MvcResult mvcResult = mockMvc
            .perform(MockMvcRequestBuilders.request(HttpMethod.POST, GET_SYS_USERS)
                .contentType(MediaType.APPLICATION_JSON).content(userRole))
            .andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$.msg").value("Success"))
            .andDo(print()).andReturn();
        String contentAsString = mvcResult.getResponse().getContentAsString();
        System.out.println(contentAsString);
        JSONObject jsonObject = JSONObject.parseObject(contentAsString);
        Assertions.assertEquals(jsonObject.get("code"), 0);
    }

}
  • 对sysUserService mock数据后的打印

在这里插入图片描述

  • 没有对sysUserService mock数据后的打印,打印数据库中的真实数据

在这里插入图片描述

2. 方式二,直接注入接口类

package com.example.handlerinterceptor.sysuser.controller;

import com.example.handlerinterceptor.dto.AjaxResult;
import com.example.handlerinterceptor.sysuser.entity.SysUser;
import com.example.handlerinterceptor.sysuser.service.SysUserService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

import javax.annotation.Resource;
import java.util.List;

@SpringBootTest
class SysUserControllerTest3 {

    @Resource
    private SysUserController sysUserController;

    @MockBean
    private SysUserService sysUserService;

    /**
     * {@link SysUserController#getSysUsers(SysUser)}
     */
    @Test
    void getSysUsers() {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(123L);
        sysUser.setRole("vip");
        Mockito.when(sysUserService.getSysUsers(ArgumentMatchers.anyString())).thenReturn(List.of(sysUser));

        AjaxResult<List<SysUser>> sysUsers = sysUserController.getSysUsers(sysUser);
        System.out.println(sysUsers);
        Assertions.assertEquals(sysUsers.getCode(), 0);
    }

}

在这里插入图片描述

mock同类中的其他方法

  • 比如需要在getUserName方法中mock saveUser方法的返回值

@Service
public class SysUserServiceImpl implements SysUserService {

    @Override
    public List<String> getUserName(String role) {
        Boolean aBoolean = this.saveUser("Fisher3652");
        if (aBoolean) {
            return List.of("Fisher3652");
        }
        return null;
    }

    @Override
    public Boolean saveUser(String name) {
        return true;
    }

}

  • 单元测试类
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;

@ExtendWith(MockitoExtension.class)
class SysUserServiceImplTest {

    @InjectMocks
    private SysUserServiceImpl sysUserService;

    /**
     * {@link SysUserServiceImpl#getUserName(String)}
     */
    @Test
    void getUserName() {
        SysUserServiceImpl spy = Mockito.spy(sysUserService);
        Mockito.doReturn(Boolean.TRUE).doReturn(Boolean.FALSE).when(spy).saveUser(ArgumentMatchers.anyString());
        List<String> userName = spy.getUserName("123");
        Assertions.assertNotNull(userName);
        List<String> userName2 = spy.getUserName("456");
        Assertions.assertNull(userName2);
    }

}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个用于构建Java应用程序的开源框架,它提供了一种简化了配置的方式来快速构建应用程序。JUnit是一个用于编写和运行单元测试的开源测试框架,而Mockito是一个用于创建和管理模拟对象的Java库。 下面是一个使用Spring BootJUnitMockito进行单元测试的示例: 假设我们有一个UserService类,它依赖于一个UserRepository接口来访问数据库并进行一些操作。我们想要对UserService的方法进行单元测试。 首先,我们需要创建一个测试类,命名为UserServiceTest。在测试类中,我们将使用JUnit的注解来标记测试方法,并使用Mockito来创建模拟对象。示例代码如下: ```java @RunWith(MockitoJUnitRunner.class) public class UserServiceTest { @InjectMocks private UserService userService; @Mock private UserRepository userRepository; @Test public void testGetUserById() { // 配置模拟对象的行为 User user = new User("1", "John"); when(userRepository.findById("1")).thenReturn(user); // 调用被测试的方法 User result = userService.getUserById("1"); // 验证结果 assertEquals("John", result.getName()); } } ``` 在上面的示例中,我们使用了@RunWith注解来指定使用MockitoJUnitRunner运行测试,这样就能自动创建和管理模拟对象。使用@InjectMocks注解将被测试的对象自动注入到测试类中,使用@Mock注解创建模拟对象。 在testGetUserById方法中,我们首先使用when方法配置userRepository模拟对象的行为,表示当传入参数为"1"时,返回一个指定的User对象。 然后,我们通过调用userService的getUserById方法来测试该方法的逻辑。最后,使用assertEquals断言来验证结果是否符合预期。 以上就是一个使用Spring BootJUnitMockito进行单元测试的示例。通过使用Mockito创建模拟对象,我们可以更容易地测试各个方法的逻辑,而不依赖于实际的数据库。这样可以提高测试效率并确保代码的质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值