Spring Boot 实践 第四章 Spring boot 的单元测试

上一章介绍了spring boot 的日志配置, 这一章将跟大家聊聊spring boot的单元测试。

spring boot 使用的是Junit作为单元测试的库. 和我们之前使用Junit没有什么本质区别,只是经过spring boot 包装过的junit, 使用起来更简单一些。好了,我们一起来看看在spring boot 的单元测试的例子吧.


 1.我们先新建一个module, 在module中新建Application.java, 跟之前的项目一样

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2.接下来再新建一个service的接口和对应的实现

DemoService.java

public interface DemoService {
    /**
     * 获取用户名
     * @return 用户名
     */
    String getUserName();
}

DemoServiceImpl.java

@Service
public class DemoServiceImpl implements DemoService {
    @Override
    public String getUserName() {
        return "test";
    }
}

3.步入正题, 针对上面这个service写个test, 先建立一个Test的基类

@SpringBootTest(classes = Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
public class BaseTest {
}

这个基类使用了三个注解,分别是什么意思呢?

@SpringBootTest 指定了主工程的入口class。

@RunWith指定JUnit的运行器, 这里使用spring boot封装过的JUnitRunner。

@ActiveProfiles 指定了环境.它的功能就像配置文件中的spring.profiles.active。

设置了这些注解, 这个基类就具有单元测试的功能了.当然在实际项目中,这里还可以定义一些日志之类的全局的变量。

4.建立一个单元测试类

我们再建立一个DemoServiceTest.java 让它继承BaseTest. 在这个Test里注入DemoService, 并对getUserName方法进行测试.代码如下:

@Slf4j
public class DemoServiceTest extends BaseTest {
    @Autowired
    private DemoService demoService;
    @Test
    public void testGetUserName () {
        String userName = demoService.getUserName();
        log.info("userName:{}", userName);
        // spring和junit的断言还是有些区别的.
        Assert.notNull(userName, "无效的用户名");
        org.junit.Assert.assertNotNull(userName, "无效的用户名");
    }
}

写好后,鼠标在测试方法上右键debug, 看看是不是可以了。在这个单元测试方法里我们使用了Spring 的断言, 它的用法和JUnit还是有区别的, 具体使用哪个,就看个人喜好了。

5.单元测试的前置和后置注解

在实际项目中,我们写单元测试的时候经常会有些数据准备等前置条件, 或者运行完单元测试后清空测试数据等操作. 那么在这里我们可以使用到 @Before @After @BeforeClass @AfterClass 等标签来完成这些工作. 我们先在上面的单元测试例子中添加一些代码:

@Slf4j
public class DemoServiceTest extends BaseTest {

    @Autowired
    private DemoService demoService;

    @Before
    public void beforeTest() {
        log.info("测试非静态前置方法");
    }

    @After
    public void afterTest() {
        log.info("测试非静态后置方法");
    }

    @BeforeClass
    public static void beforeStaticTest() {
        log.info("测试静态前置方法");
    }

    @AfterClass
    public static void afterStaticTest() {
        log.info("测试静态后置方法");
    }

    @Test
    public void testGetUserName () {
        String userName = demoService.getUserName();
        log.info("userName:{}", userName);
        // spring和junit的断言还是有些区别的.
        Assert.notNull(userName, "无效的用户名");
        org.junit.Assert.assertNotNull(userName, "无效的用户名");
    }
}

继续运行这个单元测试, 通过日志,我们就能清楚的了解到这4个标签的作用了。

@BeforeClass @AfterClass 是静态的前置后置方法, 当启动单元测试时只运行一次, 并且标注这两个注解的方法必须是static的,否则就会抛出下面异常:

java.lang.Exception: Method XXXX() should be static

@Before @After 是每个单元测试方法的前置和后置方法,运行每个单元测试时, 标注了这两个注解的方法都会运行一次。一般情况下, @BeforeClass @AfterClass 可以用作测试的环境变量, 配置信息加载.  @Before @After可以用作测试方法的数据加载和清除。 有了这几个注解,在写单元测试时就可以方便很多。

6.使用MockMvc+JUnit来进行mvc的单元测试

首先建一个Controller的demo, 代码如下:

@Slf4j
@RestController
public class DemoController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    String home() {
        log.info("Hello World!");
        return "Hello World!";
    }

    @RequestMapping(value = "/hello/{myName}", method = RequestMethod.GET)
    String index(@PathVariable String myName) {
        log.info("Hello " + myName + "!!!");
        return "Hello " + myName + "!!!";
    }

    @RequestMapping("/getUser")
    UserModel getUser(){
        UserModel userModel = new UserModel();
        userModel.setUserName("test");
        userModel.setAge(22);
        return userModel;
    }
}

这个demo中用到一个数据模型UserModel, 那就在写一个model吧,代码如下:

@Data
public class UserModel implements Serializable {

    private static final long serialVersionUID = -84121684240840394L;

    /** 用户名*/
    private String userName;

    /** 年龄 */
    private Integer age;
}

这里使用了lombok的@Data减少代码书写。(如对lombok不太了解的可以看看我写的lombok介绍和配置 )   

好了, 一个基于restful风格的spring mvc就写完了,. 这个demo中我们包含了springmvc的三种常见接口, 无参数的, 有参数的, 返回数据模型的。接下来,我们就用MockMvc针对这三种接口进行单元测试. 建立一个名叫DemoControllerTest的测试类,同样让它基础BaseTest,完成环境的引入。

因为要使用MockMvc, 所以需要在测试类的头上加上@AutoConfigureMockMvc注解,把MockMvc配置的工作交给spring boot吧。接着写三个接口的单元测试类,代码如下:

public class DemoControllerTest extends BaseTest {

    @Autowired
    private MockMvc mockMvc;

    /**
     * 无参数, 有返回值
     */
    @Test
    public void testHome() {
        try {
            mockMvc.perform(MockMvcRequestBuilders.get("/"))  //进行请求
                    .andExpect(MockMvcResultMatchers.status().isOk()) // 检查响应状态
                    .andExpect(MockMvcResultMatchers.content().string("Hello World!")); // 检查返回内容
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 有参数,有返回值
     */
    @Test
    public void testIndex() {
        try {
            mockMvc.perform(MockMvcRequestBuilders.get("/hello/test"))  // restFul的参数,直接写到uri里
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andExpect(MockMvcResultMatchers.content().string("Hello test!!!"));

            // 获取到响应, 用日志输出
            MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/hello/test")).andReturn();
            String content = result.getResponse().getContentAsString();
            log.info(content);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 无参数, json格式返回值
     */
    @Test
    public void testGetUser() {
        try {
            mockMvc.perform(MockMvcRequestBuilders.get("/getUser"))
                    .andDo(MockMvcResultHandlers.print())   // 打印请求和响应信息
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8)) // 检查响应类型
                    .andExpect(MockMvcResultMatchers.jsonPath("$.userName").value("test")) // 检查json格式的响应值
                    .andExpect(MockMvcResultMatchers.jsonPath("$.age").value(22));

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}

在这个例子中,我们用到了MockMvc的几个方法, 它们的用法如下:

  • mockMvc.perform()  : 执行请求, 参数是由MockMvcRequestBuilders.get来构建的

  • mockMvc.andExpect()  : 断言方法

  • MockMvcRequestBuilders.get(): 根据URL来构建请求, 返回一个MockHttpServletRequestBuilder

  • MockMvcResultMatchers.status(): 返回响应状态匹配器,用来断言 isOk() 表示是否200状态

  • MockMvcResultMatchers.content() : 返回响应内容匹配器

  • MockMvcResultMatchers.content().string(): 匹配字符串的返回内容

  • andReturn(): 返回响应内容

  • contentType(): 匹配响应的类型

  • jsonPath(): 匹配JSON的内容

  • andDo():执行请求后要做的事

  • print(): 打印请求和响应的信息到控制台

好了, JUnit的常用方法就介绍到这了。

 

本章结束

下一章将介绍spring boot 数据访问

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值