SpringBoot单元测试

1、SpringBoot单元测试

单元测试(Unit Test)是为了检验程序的正确性。一个单元可能是单个程序、类、对象、方法等,它是应用程序的最小可测试部件。SpringBoot提供了 spring-boot-starter-test 启动器。通过它,能引入一些有用的测试库。

1.1 快速创建单元测试

(1)在SpringBoot中进行单元测试很简单,它已经自动添加好了Test的Starter依赖,如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

(2)只要在“src/test/java”目录下新建一个测试类即可,代码如下:

import com.pjb.entity.UserInfo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * 用户业务逻辑测试类(JUnit4)
 * @author pan_junbiao
 **/
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceTest
{
    @Autowired
    private UserService userService;

    @Before
    public void setUp()
    {
    }

    @After
    public void tearDown()
    {
    }

    @Test
    public void getUserInfoById()
    {
        //测试代码
        UserInfo userInfo = userService.getUserInfoById(1);
    }
}

代码说明:

@SpringBootTest:是SpringBoot用于测试的注解,可指定入口类或测试环境等。

@RunWith(SpringRunner.class):让测试运行与Spring的测试环境。

@Test:表示为一个测试单元。

1.2 测试的回滚

在单元测试中可能会产生垃圾数据,可以开启事务功能进行回滚——在方法或者类头部添加@Transactional注解即可,代码如下:

/**
 * 新增员工
 * 加上@Transactional注解,开启事务
 * 这样测试执行完后就会进行回滚操作
 * @author pan_junbiao
 */
@Test
@Transactional
public void addStaff()
{
    //创建新用户信息
    UserInfo userInfo = new UserInfo();
    userInfo.setUserName("pan_junbiao的博客");
    userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao");
    userInfo.setBlogRemark("您好,欢迎访问 pan_junbiao的博客");

    //执行新增操作
    userDao.save(userInfo);
}

 

2、JUnit5测试框架

JUnit5是对程序代码进行单元测试的Java框架。它用来编写自动化测试工具,降低测试的难度、减少烦琐性、并有效避免出现程序错误。

2.1 JUnit5注解

所有支持的注解都在包 org.junit.jupiter.api 下;

使用@Test、@TestTemplate、@RepeatedTest、@BeforeAll、@AfterAll、@BeforeEach或@AfterEach注释的方法不能返回值。

注解说明
@Test表示方法是测试方法。与JUnit 4的@Test注释不同,这个注释不声明任何属性,因为JUnit Jupiter中的测试扩展基于它们自己的专用注释进行操作。 
@ParameterizedTest表示方法是参数化测试。
@RepeatedTest表示方法是重复测试的测试模板。
@TestFactory表示方法是动态测试的测试工厂。
@TestInstance用于为带注释的测试类配置测试实例生命周期。
@TestTemplate表示方法是为测试用例设计的模板,根据注册提供程序返回的调用上下文的数量进行多次调用。
@DisplayName声明测试类或测试方法的自定义显示名称。
@BeforeEach表示在当前类中每个@Test、@RepeatedTest、@ParameterizedTest或@TestFactory方法之前执行注释的方法;类似于JUnit 4的@Before。
@AfterEach表示在当前类中的每个@Test、@RepeatedTest、@ParameterizedTest或@TestFactory方法之后,都应该执行带注释的方法;类似于JUnit 4的@After。
@BeforeAll表示应在当前类中的所有@Test、@RepeatedTest、@ParameterizedTest和@TestFactory方法之前执行带注释的方法;类似于JUnit 4的@BeforeClass。
@AfterAll表示在当前类中,所有@Test、@RepeatedTest、@ParameterizedTest和@TestFactory方法都应该执行注释的方法;类似于JUnit 4的@AfterClass。
@Nested表示带注释的类是一个嵌套的、非静态的测试类。@BeforeAll和@AfterAll方法不能直接在 @Nested 测试类中使用,除非使用“每个类”测试实例生命周期。
@Tag用于在类或方法级别声明过滤测试的标记;类似于TestNG中的测试组或JUnit 4中的类别。
@Disabled用于禁用测试类或测试方法;类似于JUnit 4的@Ignore。
@ExtendWith用于注册自定义扩展。

2.2 JUnit5断言(Assertions类)

 JUnit5断言都是 org.junit.jupiter.api.Assertions 中的静态方法断言类。

Asser类中主要方法如下:

方法名称方法描述
assertEquals断言传入的预期值与实际值是相等的。
assertNotEquals断言传入的预期值与实际值是不相等的。
assertArayEquals断言传入的预期数组与实际数组是相等的。
assertNull断言传入的对象是为空。
assertNotNull断言传入的对象是不为空。
assertTrue断言条件为真。
assertFalse断言条件为假。
assertSame断言两个对象引用同一个对象,相当于"==”。
assertNotSame断言两个对象引用不同的对象,相当于"!=”。
assertThat断言实际值是否满足指定的条件。

【示例】SpringBoot 基于 JUnit5 的测试类。

package com.pjb;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * 用户业务逻辑测试类(JUnit5)
 * @author pan_junbiao
 **/
@SpringBootTest
public class UserServiceTest
{
    @BeforeEach
    void setUp()
    {
    }

    @AfterEach
    void tearDown()
    {
    }

    @Autowired
    private UserService userService;

    @Test
    public void getUserInfoById()
    {
        //测试代码
        UserInfo userInfo = userService.getUserInfoById(1);
    }
}

 

3、项目的单元测试

创建SpringBoot项目,实现Controller层、Service层、Dao层的单元测试。

3.1 Controller层的单元测试

Mockito是GitHub上使用最广泛的Mocking框架。它提供简洁的API来测试。Mockito简单易学、可读性强、验证语法简洁。

【示例】使用Mockito框架,对Controller层进行单元测试。

(1)创建UserController(用户信息控制器)。

package com.pjb.controller;

import com.pjb.entity.UserInfo;
import com.pjb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * 用户信息控制器
 * @author pan_junbiao
 **/
@RestController
@RequestMapping("user")
public class UserController
{
    @Autowired
    private UserService userService;

    /**
     * 获取用户信息
     * RESTful接口
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public UserInfo getUserInfo(@PathVariable("id")int userId)
    {
        UserInfo userInfo = userService.getUserInfoById(userId);
        return userInfo;
    }

    /**
     * 根据用户ID,获取用户信息
     */
    @RequestMapping("/getUserInfoById")
    public UserInfo getUserInfoById(@RequestParam(value = "user_id", defaultValue = "0") int userId)
    {
        UserInfo userInfo = userService.getUserInfoById(userId);
        return userInfo;
    }

    /**
     * 新增用户信息
     * 参数:接收对象型参数
     */
    @RequestMapping(value = "/addUserByEntity", method = RequestMethod.POST)
    public boolean addUserByEntity(@RequestBody UserInfo userInfo)
    {
        boolean result = userService.addUser(userInfo);
        return result;
    }

    /**
     * 新增用户信息
     * 参数:接收多个参数
     */
    @RequestMapping(value = "/addUserByParam", method = RequestMethod.POST)
    public boolean addUserByParam(String userName,String blogUrl,String blogRemark)
    {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserName(userName);
        userInfo.setBlogUrl(blogUrl);
        userInfo.setBlogRemark(blogRemark);

        boolean result = userService.addUser(userInfo);
        return result;
    }
}

(2)实现Controller层的单元测试。

package com.pjb.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.pjb.entity.UserInfo;
import org.junit.Assert;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
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.setup.MockMvcBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;

/**
 * 用户信息控制器测试类
 * @author pan_junbiao
 **/
@SpringBootTest
@RunWith(SpringRunner.class)
class UserControllerTest
{
    //启用Web上下文
    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

    @BeforeEach
    private void setUp()
    {
        //使用上下文构建MockMvc
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @AfterEach
    private void tearDown()
    {
    }

    /**
     * 根据用户ID,获取用户信息
     * RESTful接口
     */
    @Test
    public void getUserInfo() throws Exception
    {
        //执行请求(使用GET请求,RESTful接口)
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}",1).accept(MediaType.APPLICATION_JSON_UTF8)).andReturn();

        //获取返回编码
        int status = mvcResult.getResponse().getStatus();

        //获取返回结果
        String content = mvcResult.getResponse().getContentAsString();

        //断言,判断返回编码是否正确
        Assert.assertEquals(200,status);

        //将JSON转换为对象
        ObjectMapper mapper = new ObjectMapper();
        UserInfo  userInfo = mapper.readValue(content, UserInfo.class);

        //打印结果
        System.out.println("用户ID:" + userInfo.getUserId());
        System.out.println("用户姓名:" + userInfo.getUserName());
        System.out.println("博客地址:" + userInfo.getBlogUrl());
        System.out.println("博客信息:" + userInfo.getBlogRemark());
    }

    /**
     * 根据用户ID,获取用户信息
     * 使用GET请求
     */
    @Test
    public void getUserInfoById() throws Exception
    {
        //执行请求(使用GET请求)
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/user/getUserInfoById").param("user_id","1").accept(MediaType.APPLICATION_JSON_UTF8)).andReturn();

        //获取返回编码
        int status = mvcResult.getResponse().getStatus();

        //获取返回结果
        String content = mvcResult.getResponse().getContentAsString();

        //断言,判断返回编码是否正确
        Assert.assertEquals(200,status);

        //将JSON转换为对象
        ObjectMapper mapper = new ObjectMapper();
        UserInfo  userInfo = mapper.readValue(content, UserInfo.class);

        //打印结果
        System.out.println("用户ID:" + userInfo.getUserId());
        System.out.println("用户姓名:" + userInfo.getUserName());
        System.out.println("博客地址:" + userInfo.getBlogUrl());
        System.out.println("博客信息:" + userInfo.getBlogRemark());
    }

    /**
     * 新增用户信息
     * 使用POST请求,传递对象型参数
     */
    @Test
    public void addUserByEntity() throws Exception
    {
        //创建新用户
        UserInfo userParam = new UserInfo();
        userParam.setUserName("pan_junbiao的博客");
        userParam.setBlogUrl("https://blog.csdn.net/pan_junbiao");
        userParam.setBlogRemark("您好,欢迎访问 pan_junbiao的博客");

        //将参数转换成JSON对象
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(userParam);

        //执行请求(使用POST请求,传递对象参数)
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/user/addUserByEntity").content(json).contentType(MediaType.APPLICATION_JSON)).andReturn();

        //获取返回编码
        int status = mvcResult.getResponse().getStatus();

        //获取返回结果
        String content = mvcResult.getResponse().getContentAsString();

        //断言,判断返回编码是否正确
        Assert.assertEquals(200,status);

        //断言,判断返回结果是否正确
        Assert.assertEquals("true",content);
    }

    /**
     * 多个参数的传递
     * 使用POST请求,传递多个参数
     */
    @Test
    public void addUserByParam() throws Exception
    {
        //多个参数的传递
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("userName","pan_junbiao的博客");
        params.add("blogUrl","https://blog.csdn.net/pan_junbiao");
        params.add("blogRemark","您好,欢迎访问 pan_junbiao的博客");

        //执行请求(使用POST请求,传递多个参数)
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/user/addUserByParam").params(params)).andReturn();

        //获取返回编码
        int status = mvcResult.getResponse().getStatus();

        //获取返回结果
        String content = mvcResult.getResponse().getContentAsString();

        //断言,判断返回编码是否正确
        Assert.assertEquals(200,status);

        //断言,判断返回结果是否正确
        Assert.assertEquals("true",content);
    }
}

执行结果:

3.2 Service层的单元测试

【示例】Service层进行单元测试,使用Assert方法进行断言结果。

package com.pjb.service;

import com.pjb.entity.UserInfo;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * 用户业务逻辑测试类
 * @author pan_junbiao
 **/
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceTest
{
    @Autowired
    private UserService userService;

    @Test
    public void getUserInfoById()
    {
        //获取用户信息
        UserInfo userInfo = userService.getUserInfoById(1);

        //断言
        Assert.assertEquals(1,userInfo.getUserId());
        Assert.assertEquals("pan_junbiao的博客",userInfo.getUserName());
        Assert.assertEquals("https://blog.csdn.net/pan_junbiao",userInfo.getBlogUrl());
        Assert.assertEquals("您好,欢迎访问 pan_junbiao的博客",userInfo.getBlogRemark());
    }
}

3.3 Dao层的单元测试

【示例】Dao层进行单元测试,以及使用@Transactional注解进行回滚操作。

import com.pjb.entity.UserInfo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;

/**
 * 用户信息数据库访问测试类
 * @author pan_junbiao
 **/
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserDaoTest
{
    @Autowired
    private UserDao userDao;

    /**
     * 新增员工
     * 加上@Transactional注解,开启事务
     * 这样测试执行完后就会进行回滚操作
     * @author pan_junbiao
     */
    @Test
    @Transactional
    public void addStaff()
    {
        //创建新用户信息
        UserInfo userInfo = new UserInfo();
        userInfo.setUserName("pan_junbiao的博客");
        userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao");
        userInfo.setBlogRemark("您好,欢迎访问 pan_junbiao的博客");

        //执行新增操作
        userDao.save(userInfo);
    }
}

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pan_junbiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值