zoukankan      html  css  js  c++  java
  • 如何写好单元测试

          代码的单元测试非常重要,是团队开发中必不可少的一环!这个懂的人,应该无需赘言了吧!!!(重要的事情说三遍)

          那如何才能写好单元测试呢?网上提到过很多优秀的项目单元测试必须满足的原则,诸如覆盖率,完整性,自动化等等。今天我想从为什么写不好单元测试,甚至是无法写出单元测试的理由入手,给出一些个人的建议与方法,希望能给大家一些启发和思考。

          为什么写不好单元测试呢?很多人给出的理由是,系统的耦合性太高了,为了测试一个接口,需要考虑太多东西了。以我们项目的实际为例,业务层的接口实现调用了事务控制层的接口,然后事务控制层的接口实现又调用了DAO层的接口,一层扣一层,真是不好写啊!!

          要写好这类代码的单元测试,普世的原则自然是解耦了。想必道理大家都懂,但是真正做起来就很麻烦了。其实,我的总结是,各层既然有明确的分工和职责,那单元测试关注的点应该也是自身的职责所在。还是以我们的项目为例,业务层的职责是面向过程的代码过程,每个接口实现的是业务流转和逻辑,业务层并不关心数据怎么存储和读取(这不应该是业务层应该关心的)。业务层的单元测试也应该服务于此,比如一个保存的接口,我们只需要测试其要保存的对象数据是否完整,校验规则是否能照顾到。有人可能问,如果这个接口有返回值,当然好验证了,那没有返回值的情况怎么验证呢?这个时候我们就需要用到mock的思想了。

          话不多说,上点代码吧。

          要测试业务层这个接口如下:

    public RemoteResult<Boolean> savePushContent(final PushContentApi pushContentApi) {
            return RemoteServiceInvoker.doRunWithExHandler(new RemoteObject<Boolean>() {
                @Override
                public RemoteResult<Boolean> run() {
                    RemoteResult<Boolean> result = new RemoteResult<Boolean>(true);
                    pushContentApi.validateAddBean();
                    if (TYPE_FASTPUSH.equals(pushContentApi.getType())) {
                        pushContentApi.setSerialNo(SERIALNOFAST_PRE + IdGenerator.genSerialNoWithDateAndRandom2());
                    } else {
                        pushContentApi.setSerialNo(SERIALNO_PRE + IdGenerator.genSerialNoWithDateAndRandom2());
                    }
                    pushContentApi.setSendState(SEND_STATE_WAIT_CONFIRM);
                    setSendInfo(pushContentApi);
    
                    pushContentManager.savePushContent(BeanCopyUtil.createAndCopy(pushContentApi, PushContent.class));
                    result.setT(true);
                    return result;
                }
            });
        }
    

      代码里调用了pushContentManager的接口。我们mock一下这个manager,然后实现一下savePushContent一下。

    @Override
        public void savePushContent(PushContent pushContent) {
            assertNotNull(pushContent);
            assertNotNull(pushContent.getSerialNo());
            assertEquals("测试内容", pushContent.getContent());
            assertEquals(Long.valueOf(-1), pushContent.getCreatorId());
            assertEquals(PushContentConstants.SEND_STATE_WAIT_CONFIRM, pushContent.getSendState());
            if (pushContent.getTaskId() == null) {
                assertEquals("13810001000,13810001001", pushContent.getTestMobiles());
                assertEquals(Long.valueOf(2), pushContent.getSendNum());
            } else if (MockDataTaskManager.DATA_TASK_ID.equals(pushContent.getTaskId())) {
                assertEquals(Long.valueOf(201), pushContent.getActivityId());
                assertEquals(Long.valueOf(100), pushContent.getSendNum());
            }
        }
    

      然后单元测试如下:

    @Test
        public void testSavePushContent() throws Exception {
            // 测试关联数据任务的内容
            PushContentApi pushContentApi = new PushContentApi();
            pushContentApi.setContent("测试内容");
            pushContentApi.setActivityId(201l);
            pushContentApi.setCreateTime(new Date());
            pushContentApi.setCreatorId(-1l);
            pushContentApi.setCreatorName("测试用户");
            pushContentApi.setTaskId(MockDataTaskManager.DATA_TASK_ID);
            pushContentApi.setUpdateTime(new Date());
            pushContentApi.setUpdatorId(-1l);
            pushContentApi.setUpdatorName("测试用户");
            pushContentSoaService.savePushContent(pushContentApi);
            // 测试不关联数据任务的内容
            PushContentApi pushContentApi1 = new PushContentApi();
            pushContentApi1.setContent("测试内容");
            pushContentApi1.setCreateTime(new Date());
            pushContentApi1.setCreatorId(-1l);
            pushContentApi1.setCreatorName("测试用户");
            pushContentApi1.setTestMobiles("13810001000,13810001001");
            pushContentApi1.setUpdateTime(new Date());
            pushContentApi1.setUpdatorId(-1l);
            pushContentApi1.setUpdatorName("测试用户");
            pushContentSoaService.savePushContent(pushContentApi1);
        }
    

      验证的逻辑都放在了mock出来的manager的接口实现里,跑一下这个单元测试,就能验证正确的逻辑了。这个只是很简单的代码思路,业务层接口实现里如果有很多校验规则与逻辑的话,还需要分别测试。如果要测试真实的manager接口实现,那又再单独写他的单元测试了,但是应该是与业务层的单元测试完全解耦的。

          总结一下,单元测试要想写好,必须要灵活运用解构技巧,关注点始终放在各层的分工与职责上。而且有了这种思想,就能实现真正的测试驱动开发了(测试用例先行,你懂的)。

          

      

  • 相关阅读:
    程序员自我【营销】,如何打造个人【品牌】
    程序员应该怎样和领导相处?
    程序员必备能力——晋升之道
    聊一聊 软件系统中的“热力学第二定律”
    程序员如何利用技术管理技巧
    技术人必须掌握能力——深度思考
    程序员逆袭之路——系列文章更新中
    程序员跳槽,该如何选择一家好公司
    C++-运行时类型信息,异常(day11)
    C++-多态,纯虚函数,抽象类,工厂模式,虚析构函数(day10)
  • 原文地址:https://www.cnblogs.com/kaitokidzhao/p/6439436.html
Copyright © 2011-2022 走看看