• 正式学习单元测试


    单元测试学习总结

    简单记录自己在单元测试上的弯路和一些实践。

    1. 为什么需要单元测试

    以前写代码的时候总是没有写单元测试的习惯,总觉得项目启动后,跑一下就好了,反正总是要和前端联调,自己跑一下接口没有什么坏处。

    而在一家大公司之后,各种标准化的代码方式规范了起来,当然负担也是更重了。但是写简单的单元测试可以帮你发现很多小的问题;从另一方面来说,程序员应该要对自己的代码负责,即使出现了bug,也不是测试的问题,应该是开发没有把代码考虑完全造成的。

    《精通spring4.x》中对单元测试的必要性说了几点理由:1. 单元测试的好处

    1. 软件质量的保证
    2. 可以优化目标代码段设计
    3. 是代码重构的保障(改造之后可以快速验证行为一致性)
    4. 是回归测试和持续集成的基础

    2. 最初级的单元测试

    虽然单元测试有以上这些好处,但确实是码代码过程中很枯燥的部分,也许是自己还没有领悟到TDD的好处吧。其中关于优化代码的好处,目前还没有领悟到。

    所以刚开始做单元测试的时候,仅仅是把对应的待测试对象引入,然后调用方法,最后打印结果,通过肉眼的方式来判断是否出错了。

    当然,运行时异常之类的异常还是可以排查出来的,所以,也算是把流程走了一遍吧。

    目前看来太low了。自己本来就是为了简化工作流程的工作,把线下的工作搬到线上,但是在实现的过程却还是使用纯人力的方式,完全对不起程序员这个称呼,果然是码农哦。

    这个阶段,我对单元测试的要求是针对所有和前端交互的接口对进行测试,目标是把主流程过一遍,所以使用的是 mockMVC 的方式,基类如下:

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = ApplicationServer.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
    @ActiveProfiles(profiles = "test")
    @Transactional
    @Rollback
    public class AbstractControllerTest<T> {
    
      protected MockMvc mockMvc;
    
      protected void setUp(T controller) {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
      }
    
      protected void printResult(MvcResult mvcResult, String name) {
        String result = null;
        try {
          result = mvcResult.getResponse().getContentAsString();
        } catch (UnsupportedEncodingException e) {
          e.printStackTrace();
        }
        System.out.println(String.format("=====> %s: %s", name, result)); // 肉眼可见的鄙视
      }
    
    }
    

    3. 次级的单元测试

    后来看了别人写的单元测试,后知后觉之后,开始使用断言来对程序的结果进行判断,直接通过红绿来判断代码是否通过测试即可。

    随即增加的还有针对service进行测试,因为实现的细节还是更重要的,而不仅仅只是主流程通过即可。另一方面,当测试覆盖率的要求上来之后,仅仅controller的测试就不够了。

    然后就是针对service的测试,因为大家的习惯都是把逻辑都写在service 中,而controller作为接入层,仅仅只调用service即可。

    而service的测试经常伴随着数据库的访问,这时候还需要自己准备一些数据之后,才能够进行测试,测试基类如下:

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
            properties = {"logging.path=logs",
                    "spring.profiles.active=test"
            })
    @Transactional
    @Rollback
    public class BaseTest {
    	@Autowired
    	UserDao userDao;
    	
    	@Test
    	public void testLogin(){
    		User user=userDao.findByUser(1);
    		assertNotNull(user);
    		assertEquals(user.password,"xxxx");  // 这些数据一看就是自己提前写好的。。。
    	}
    }
    

    4. 可重复的单元测试

    由2-3的进步,差不多可以应付过单元测试的编写了,因为毕竟也可以覆盖大部分的测试场景。

    但是你会发现,3中的单元测试其实是不具有可重复执行的要求的。如果你写的单元测试不具有可重复执行的特性,那么你还不如不写单元测试,直接启动项目之后,把所有功能都回归一遍就好了,根本不用浪费时间来写无聊的代码。

    那么针对数据库的测试如何做到可重复执行呢,毕竟测试数据库又不是自己的,没有办法控制别人什么时候会把你的测试数据删掉,导致下次执行的时候就报错了。

    这里有两种方案:

    • mock 的方式,即把通过数据库返回的逻辑,手动模拟成正确的,这样你就不用依赖数据库,但同时你就缺少了一环。

    测试代码如下:

    	@Test
        public void test(){
            doReturn("success").when(userDao).findByUser(1);
    
            String str = userDao.findByUser(1);
    		 assertEquals(str,"success");
        }	
    

    这种方法很简单,自己去构造数据,可以测试代码逻辑是否符合你的要求。

    • 使用dbunit模拟数据

    先上代码:

    @DatabaseSetup("/table.xls")
      @Test
      public void listUser() {
        List<Integer> levels = Lists.newArrayList();
        List<Integer> states = Lists.newArrayList();
        Integer page = 1;
        Integer pageSize = 10;
        Pagination<ProjectListVO> pagination = userService.listUser(operator, levels, states, page, pageSize);
        assertNotNull(pagination);
        assertEquals(pagination.getTotalCount(), 3);
      }
    

    可以看到和之前没有什么不一样,只是方法上增加了一个注解,com.github.springtestdbunit.annotation.DatabaseSetup,这样就可以在 xls 文件里去构建你的数据库数据,然后只需要保证这个文件不被修改就可以了,可以每个开发一个自己的测试文件,美滋滋。

    以上,基本上解决了 接入层、dao层、代码逻辑的各种测试,也很简单去完成,但是简单的测试不仅仅是为了应付要求,还要真正理解自己的代码,然后逼迫自己去写出小而美的代码。

    目前仅仅知道了表面的方法,方法背后的意义还需要慢慢体会学习。

    ps:以上引用的注解使用了这个开源组件,有兴趣的同学可以访问学习,很棒。

    5. 文章集合:

    有赞的特别推荐!!!

    单元测试实践经验
    有赞单元测试时间
    TDD及单元测试最佳实践
    spring Boot测试的最佳实践和测试架构的启发(JUnit4和mockito,包括MockMvc
    Java服务端单元测试指南

  • 相关阅读:
    YII中表单验证
    YII中的表单挂件
    YII数据库操作(CURD操作)
    YII中的session和cookie
    YII中面包屑制作(当前位置:网站首页 >> 会员登陆)
    ios NSFileManager 用法详解
    iOS沙盒路径的查看和使用
    iOS7改变状态栏文字颜色
    iOS常用第三方库 -转
    Linux下mysql新建账号及权限设置各种方式总结
  • 原文地址:https://www.cnblogs.com/paxing/p/12835479.html
Copyright © 2020-2023  润新知