美文网首页初见
如何对Springboot项目进行单元测试

如何对Springboot项目进行单元测试

作者: qinghaihu | 来源:发表于2020-04-11 20:41 被阅读0次

    1 为什么需要单元测试

    在平时的开发当中,一个项目往往包含了大量的方法,可能有成千上万个。如何去保证这些方法产生的结果是我们想要的呢?

    传统解决方案:Postman 发报文,System.out打印debug日志,或者眼睛看返回报文

    1、眼睛看结果是否正确,瞅瞎不说,也太不智能.我们是高智商程序员,能让代码解决的事情,绝不能靠人工去解决.

    2、postman 只能对controller进行测试。controller要正确,前提是service,dao都正确。发现问题太晚,解决成本高。

    3、对于一些交易系统,由于交易主键的存在,每次都要更改参数后,再进行测试,效率太低。

    4、无法对内部的函数功能做测试

    5、postman的测试案例与项目工程不再一起,这些案例只能自己一个人用,无法团队共享

    这时,就轮到单元测试闪亮出场了。

    • 测试代码和工程代码在同一工程文件中,便于维护和传承。

    • 使用断言自动检测结果

    • 测试粒度小,可以小到每个函数

    • 测试模块间相互依赖小。开发完一个模块,就可以测试一个模块。妈妈再也不用担心我犯大错了。

    2 如何做单元测试

    2.1 单元测试框架基础

    业界单元测试一般采用基于JUNIT和MockMvc 框架进行, 其中JUNIT 是通用测试框架,主要进行Dao层和Service层测试。MockMvc主要进行Controller层测试.

    2.1.1 Junit基本概念

    Junit的理解其实很简单,用起来也很方便。关键在于理解几个注解。

    • @Test:将一个普通方法修饰成一个测试方法,以前我们运行程序,需要main方法,而现在,可以直接运行一个被@Test修饰的方法。
      • @Test(excepted=xx.class): xx.class表示异常类,表示测试的方法抛出此异常时,认为是正常的测试通过的
      • @Test(timeout=毫秒数) :测试方法执行时间是否符合预期
    • @BeforeClass: 会在所有的Class方法执行前被执行,static方法
    • @AfterClass:会在所有的Class方法执行之后进行执行,static方法。通常用于清理工作。
    • @Before:会在每一个@Test测试方法被运行前执行一次
    • @After:会在每一个@Test测试方法运行后被执行一次。通常用于资源关闭。
    • @Ignore:所修饰的测试方法会被测试运行器忽略
    • @RunWith:可以更改测试运行器org.junit.runner.Runner。

    总结一下,运行顺序为 BeforeClass---->Before----->Test----->Ater----->AfterClass

    2.2 Springboot框架单元测试实战

    2.2.1 Maven 引包

    pom.xml文件中添加spring-boot-starter-test 依赖

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

    该依赖为springboot 的stater,会引入一系列相关包。

    Unit Test Jar.png

    2.2.2 新建测试源码目录

    • test/java 存放test *.java文件
    • test/resourse 存放yml配置文件

    新建好目录后,需要在project structrue中Mark这两个资源目录,否则工程会无法正常识别加载这些文件。

    project structrue.png

    2.2.3 编写单元测试案例

    建议:为了让工程目录友好,可以根据不同的服务,新建不同的pakage。Dao、Service和Controller各新建一个*.java文件。

    2.2.3.1 编写测试基类
    package com.qinghaihu.springbootmybatis;
    
    
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.annotation.Rollback;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.transaction.annotation.Transactional;
    
    @SpringBootTest
    @Transactional
    @Rollback
    @RunWith(SpringRunner.class)
    public class SpringbootUnitTest {
    
    
    }
    

    解释

    1、@SpringbootTest @RunWith(SpringJUnit4ClassRunner.class)

    让测试在Spring容器环境下执行测试

    2、@Transactional 开启事务,支持工程代码中配置了的注解式事务
    3、@Rollback 设置测试后回滚,默认属性为true,即回滚。保证测试完后,自测数据不污染数据库,从而保证测试案例可以重复执行。

    2.2.3.2 编写三层模型测试代码

    Springboot项目采用经典三层模型搭建,即dao,service,controller。开发顺序通常是这样,测试顺序也是一般按照这个顺序进行。

    从单元测试实践经验来说,dao层单元测试通常不会强制要求,而service和controller则必须进行。

    2.2.3.2.1 Service层 测试
    package com.qinghaihu.springbootmybatis.user;
    
    import com.qinghaihu.springbootmybatis.SpringbootUnitTest;
    import com.qinghaihu.springbootmybatis.dao.SysUserDao;
    import com.qinghaihu.springbootmybatis.entity.SysUser;
    import com.qinghaihu.springbootmybatis.service.SysUserService;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.Date;
    
    import static org.junit.jupiter.api.Assertions.*;
    
    /**
     * @ClassName UserServiceTest
     * @Description TODO
     * @Author:Zhang Lianzhong
     * @Date 2020/4/11 5:12 下午
     * @Version 1.0
     **/
    
    public class UserServiceTest extends SpringbootUnitTest {
    
        @Autowired
        SysUserService sysUserService;
    
        @Autowired
        SysUserDao sysUserDao;
    
    
        @Test
        public void testInsert(){
            SysUser sysUser = new SysUser();
            String id = "5";
            sysUser.setId(id);
            sysUser.setUsername("qinghaihudream");
            sysUser.setPassword("88888887");
            sysUser.setCreatetime(new Date());
            sysUser.setUserdesc("奔跑吧程序猿");
            sysUser.setUserimg("/user/images/qinghaihu.jpg");
            sysUser.setUserstate(-1);
    
            boolean insertResult = sysUserService.insertUserInfo(sysUser);
            assertTrue(insertResult);
    
            SysUser queryedUser = sysUserDao.selectByPrimaryKey(id);
            assertEquals(queryedUser.getUsername(),sysUser.getUsername());
        }
    
        @Test
        public  void testSelect(){
            SysUser sysUser = new SysUser();
            String id = "5";
            sysUser.setId(id);
            sysUser.setUsername("qinghaihudream");
            sysUser.setPassword("88888887");
            sysUser.setCreatetime(new Date());
            sysUser.setUserdesc("奔跑吧程序猿");
            sysUser.setUserimg("/user/images/qinghaihu.jpg");
            sysUser.setUserstate(-1);
    
            int insertResult = sysUserDao.insert(sysUser);
            assertEquals(insertResult,1);
    
            SysUser queryedUser = sysUserService.selectById(id);
            assertEquals(queryedUser.getUsername(),sysUser.getUsername());
    
        }
    
        @Test
        public void testUpdate() {
    
            //准备测试数据
            String id = "4";
            String orgUserDesc = "你好程序猿";
            String updateUserDesc = "奔跑吧程序猿";
    
            SysUser orgSysUser = new SysUser();
            orgSysUser.setId(id);
            orgSysUser.setUsername("qinghaihu");
            orgSysUser.setPassword("88889998");
            orgSysUser.setCreatetime(new Date());
            orgSysUser.setUserdesc(orgUserDesc);
            orgSysUser.setUserimg("/user/images/qinghaihu.jpg");
            orgSysUser.setUserstate(-1);
            int insertCount = sysUserDao.insert(orgSysUser);
            assertEquals(insertCount,1);
    
            SysUser updateSysUser = new SysUser();
            updateSysUser.setId(id);
            updateSysUser.setUsername("qinghaihu");
            updateSysUser.setPassword("88889998");
            updateSysUser.setCreatetime(new Date());
            updateSysUser.setUserdesc(updateUserDesc);
            updateSysUser.setUserimg("/user/images/qinghaihu.jpg");
            updateSysUser.setUserstate(-1);
    
    
            boolean updateResult = sysUserService.updateUserInfo(updateSysUser);
            assertTrue(updateResult);
    
            //检查数据是否更新
            SysUser queryedUser = sysUserService.selectById(id);
            assertEquals(queryedUser.getUserdesc(),updateUserDesc);
    
    
        }
    
        @Test
        public void testdelete() {
            SysUser sysUser = new SysUser();
            String id = "5";
            sysUser.setId(id);
            sysUser.setUsername("qinghaihudream");
            sysUser.setPassword("88888887");
            sysUser.setCreatetime(new Date());
            sysUser.setUserdesc("奔跑吧程序猿");
            sysUser.setUserimg("/user/images/qinghaihu.jpg");
            sysUser.setUserstate(-1);
    
            boolean insertResult = sysUserService.insertUserInfo(sysUser);
            assertTrue(insertResult);
    
            boolean deleteResult = sysUserService.deleteById(id);
            assertTrue(deleteResult);
    
            //检查数据是否删除
            SysUser queryedUser = sysUserService.selectById(id);
            assertNull(queryedUser);
    
        }
    
    
    }
    
    
    2.2.3.2.2 Controller层测试
    package com.qinghaihu.springbootmybatis.user;
    
    import com.qinghaihu.springbootmybatis.SpringbootUnitTest;
    import com.qinghaihu.springbootmybatis.dao.SysUserDao;
    import com.qinghaihu.springbootmybatis.entity.SysUser;
    import org.junit.Before;
    import org.junit.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.RequestBuilder;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    
    import java.util.Date;
    import static org.hamcrest.Matchers.is;
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
    /**
     * @ClassName UserControllerTest
     * @Description TODO
     * @Author:Zhang Lianzhong
     * @Date 2020/4/11 6:13 下午
     * @Version 1.0
     **/
    public class UserControllerTest extends SpringbootUnitTest {
    
        @Autowired
        SysUserDao sysUserDao;
    
        @Autowired
        protected WebApplicationContext wac;
    
        private MockMvc mvc;
    
        @Before
        public void setUp() throws Exception {
            mvc = MockMvcBuilders.webAppContextSetup(wac).build();   //初始化MockMvc对象
        }
    
    
    
        @Test
        public void testQuery() throws Exception {
            //准备测试数据
            String id = "4";
            String userName = "qinghaihu";
            SysUser orgSysUser = new SysUser();
            orgSysUser.setId(id);
            orgSysUser.setUsername(userName);
            orgSysUser.setPassword("88889998");
            orgSysUser.setCreatetime(new Date());
            orgSysUser.setUserdesc("奔跑吧程序猿");
            orgSysUser.setUserimg("/user/images/qinghaihu.jpg");
            orgSysUser.setUserstate(-1);
            int insertCount = sysUserDao.insert(orgSysUser);
            assertEquals(insertCount,1);
    
    
            RequestBuilder request = null;
    
            /*
             * 1、MockMvcRequestBuilders.post("XXX")构造一个post请求。
             * 2、mockMvc.perform执行一个请求。
             * 3、ResultActions.param添加请求传值
             * 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE)设置返回类型
             * 5、ResultActions.andExpect添加执行完成后的断言。
             * 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
             *    比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
             * 7、ResultActions.andReturn表示执行完成后返回相应的结果。
             */
            //post提交一个people
            request = post("/SysUser/query")
                    .param("id",id);
            String json = mvc.perform(request)
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.username",is(userName)))
                    .andDo(print())
                    .andReturn().getResponse().getContentAsString();
    
        }
    
    }
    
    

    通过@Autowired WebApplicationContext wac:注入web环境的ApplicationContext容器
    在@Before修饰的方法中通过MockMvcBuilders.webAppContextSetup(wac).build()创建一个MockMvc 进行测试。

    3 工程源码

    https://github.com/zhanglianzhong/sprinboot-tech.git

    相关文章

      网友评论

        本文标题:如何对Springboot项目进行单元测试

        本文链接:https://www.haomeiwen.com/subject/cmrpmhtx.html