单元测试的窘境

        大家都知道单元测试的重要,网上也有很多讲单元测试的文章,但是脱离网上的case实际维护用例的时候就不是那么回事了。我们团队从17年开始摸索单测的实行,发现了在落地过程中的一些问题。

       首先测试的类型是不一样的,先讲几种对单测比较友好的类型:第一种DAO层的单测,DBunit +SpringTest 事务回滚算是豪华套餐了,需要解决的问题就是验证sql是否符合预期。这种类型的单测成本低,效果好,性价比很高。第二种针对中间件的单测,主要是验证逻辑是否符合预期。这类场景因为需求明确,逻辑这部分很少变动,加上对中间件的严格要求,单测基本上是标配。

       日常中接触到的很多时候是另一种场景:业务层(Service层)的逻辑,需要调用DAO层另外可能还有一些远程服务再加上本身的业务逻辑。业界有比较成熟的mock框架可以解决远程服务的依赖,当然也可以解决DAO层的,但是有一个很重要的问题,数据之间往往都是有关联的,也就是说在构造mock数据的时候,你需要维护他们之间的关系,如果mock的对象有很多字段的时候,你会发现维护mock的代码比实际验证的代码要长很多,再加上为了验证逻辑里的调用关系,需要通过verify来验证mock 的远程服务是否如预期的被调用。可以看到要想实现一套业务层的单测成本还是略(hen)高的。类似下面的单测代码:

@Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        init();
    }

    private void init () {
        timeout.setBizId(BIZ_ID);
        timeout.setAppName(APP_NAME);
        timeout.setType(TimeOutTypeEnum.TOC_TYPE_TERMINATOR.getId());
        timeout.setTopicKey("test-topic-key");
        timeout.setTopicTag("test-topic-tag");
        timeout.setTopicName(TEST_TOPIC_NAME);
        timeout.setCronExp("*/5 * * * * ?");
        timeout.setTaskType(11);

    }


    @Test
    public void testAddAndStartCronTask() {
        timeout.setTaskType(null);
        int taskTypeId = 112211221;
        CronTaskBO cronTaskBO = new CronTaskBO();
        long taskId = new Date().getTime();
        long cronTaskId = new Date().getTime();
        cronTaskBO.setId(taskId);
        TaskTypeBO taskTypeBO = new TaskTypeBO();
        taskTypeBO.setId(taskTypeId);

        when(taskTypeProxyManager.getTaskTypeBOByTopicName(TEST_TOPIC_NAME)).thenReturn(taskTypeBO);
        when(cronTaskManager.getCronTaskByUniqQuery(isA(CronTaskUniqQuery.class))).thenReturn(cronTaskBO);
        //spy 使用when还是会真实调用,要想不走方法体可以使用doReturn
        //when(cronTaskService.insertOrUpdate(isA(CronTaskForm.class))).thenReturn(cronTaskId);
        doReturn(cronTaskId).when(cronTaskService).insertOrUpdate(isA(CronTaskForm.class));
        CronTaskVO cronTaskVO = new CronTaskVO();
        doReturn(cronTaskVO).when(cronTaskService).startCronTask(cronTaskId);

        CronTaskVO result = cronTaskService.addAndStartCronTask(timeout);

        verify(taskTypeProxyManager).getTaskTypeBOByTopicName(TEST_TOPIC_NAME);
        //这里使用参数捕获器来进行参数校验
        ArgumentCaptor<CronTaskUniqQuery> queryCaptor = ArgumentCaptor.forClass(CronTaskUniqQuery.class);
        verify(cronTaskManager).getCronTaskByUniqQuery(queryCaptor.capture());
        CronTaskUniqQuery cronTaskUniqQuery = TimeOutBO.toQueryFromBO(timeout);
        //使用unitils 的反射断言
        assertReflectionEquals(cronTaskUniqQuery, queryCaptor.getValue());

//        assertThat(cronTaskVO).isEqualTo(result);
    }

其实就是为了测试cronTaskService.addAndStartCronTask ,对应的代码是

 @Override
    @Transactional
    public CronTaskVO addAndStartCronTask(TimeOutBO timeOutBO) {
        // 设置任务类型
        if (Objects.isNull(timeOutBO.getTaskType()) && StringUtils.isNotBlank(timeOutBO.getTopicName())) {
            TaskTypeBO taskTypeBO = taskTypeProxyManager.getTaskTypeBOByTopicName(timeOutBO.getTopicName());
            timeOutBO.setTaskType(taskTypeBO.getId());
        }
        // cron任务唯一性校验
        CronTaskBO cronTaskBO = cronTaskManager.getCronTaskByUniqQuery(TimeOutBO.buildQueryFromBO(timeOutBO));
        CronTaskForm cronTaskForm = TimeOutBO.buildFormFromBO(timeOutBO);
        if (cronTaskBO != null) {
            cronTaskForm.setId(cronTaskBO.getId());
        }
        //fixit 跟上面getTaskTypeByTopicName的部分内容有重复
        Long cronTaskId = insertOrUpdate(cronTaskForm);
        return startCronTask(cronTaskId);
    }

粗略的估算要想有较好的覆盖率,单测的代码量是业务代码的2~3倍。而且业务层的代码往往容易重构和调整,一旦代码发生变化测试用例基本上就废的七七八八了。这个对快节奏的互联网公司来说太奢侈了,所以会看到就算一时投入“巨资”维护好的单测,经过几次日常之后就毁的差不多了。这个也是为什么业务层的单测一直没能推行起来的原因,不是说单测解决不了问题,而是这个成本实在太高。可以看到这类的单测跟上面提到的二类单测明显存在的区别:单纯的DAO层的单测因为只依赖数据而且是简单的数据,没有数据之间的关联,中间件的单测主要核心是代码逻辑,可以看到他们基本上都是一维的,就是要么跟数据相关要么跟逻辑相关。一维的简单性也是白盒测试比较容易解决的,但是业务代码就不一样,很多业务代码都是二维的,就是跟逻辑和数据都有关系,而且数据之间还存在关联。这就导致白盒测试的复杂度大大增加。

      实际工程中,往往不会写单纯的单元测试,通常会将业务层整个串起来,跑一个功能测试来验证最后的结果是否正确。现在代码中的很多“单测”就是这样的。为什么会出现这样的单测,因为考虑到严格意义的单测成本巨大,但是后端开发同学又要保证自己提供服务的正确性,往往会进行相应的折衷, 在一个容器中调用一下服务看下整条链路是不是通的, 最终的返回结果有没有值。这样存在的问题是测试往往是一次性的,因为代码里硬编码了数据(比如商品id),这个数据关系对应到数据库中下次可能就会发生变化(商品修改了)。通常是开发用来冒烟自己功能,这样的测试用例在工程中没有第二次作用,对覆盖率没有任何帮助。而且就算是这样的一次性冒烟用例,维护的成本也在不断上升。首先是时间问题:随着工程大了,Spring加载的内容越来越多,要跑一个用例花的时间不少。还有一个是依赖问题,有时候你不需要依赖外部的一个服务,但是配置都在spring的上下文,对不起你也必须等那个服务加载好,遇到外部服务挂了,你的spring也会启动不了。这个时候有人会说可以维护一份独立的spring上下文给Test 代码用,是的确实可以这样,但是你这样做了之后你会发现依赖是嵌套的,你想要测试的代码是不依赖那个外部服务,但是那个bean依赖了,你不依赖就报错了,最后的结果是你为了让spring 容器起来不得不依赖那个你不需要的外部服务,当然你也可以mock掉,但是有很多这样的外部服务时,工作量不小。

       总结一下,我们的单测遭遇的尴尬:1.实际的业务场景依赖数据以及数据之间的关联关系;2.白盒测试对业务层代码来说成本太高,而且很容易过时;3.开发自己折腾的“冒烟单测”能验证当时程序的正确性,属于一次性消费品,而且依赖外部服务spring 容器启动时间较长。针对上面的这些问题,我们通过什么方法进行解决,请期待下一篇《单元测试的解答》

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
安吉物流方案设计 班级: 目录 背景 3 一、宗旨目标 3 二、物流模式 3 2.1、需求分析 3 2.2、问题分析 3 2.3总体物流模式 3 2.4、网络结构 3 2.5、其他建议 3 三、安吉汽车物流线路优化 3 3.1运输线路问题的分析 3 3.2模式和运输方式、路径的选择 3 四、资源调度计划 3 4.1针对案例7的问题分析 3 4.2、调度模式与方法 3 4.3、调度作业流程设计 3 五、滚装物流模式 3 5.1、车滚装物流的概念 3 5.2、滚装物流在整车物流中的适用性分析 3 5.3、滚装物流运作方式 3 5.4其他建议 3 六、组织架构 3 七、附录 3 背景 安吉汽车物流有限公司成立于 2000 年 8 月,是上汽集团旗下的全资子公司。安吉物流是全球业务规模最大的汽车物流服务供应 商,共有员工17,000人,拥有船务、铁路、公路等10家专业化的轿车运输公司以及50家 仓库配送中心,仓库总面积超过440万平方米,年运输和吞吐量超过570万辆商品车,并 且全部实现联网运营。公司以"服务产品技术化"的理念,从事汽车整车物流、零部件物 流、口岸物流以及相关物流策划、物流技术咨询、规划、管理培训等服务。提供一体化 、技术化、网络化、透明化、可靠的独特解决方案的物流供应链服务。 图1 安吉物流作为一家为汽车及零部件制造企业提供服务的第三方物流公司,下属业务包 括整车物流、零部件物流、口岸物流等三大业务板块(图1),客户包括上海大众、上海 通用、上汽通用五菱、一汽丰田、广汽丰田、比亚迪等几乎国内所有主机厂。2011年汽 车物流量达574万辆,营业收入达132亿元,取得了业务量、收入、市场占有率均排名国 内同行第一的骄人业绩,在国际同行中也名列前茅。目前,安吉物流是中国物流与采购 联合会汽车物流分会轮席理事长单位,5A级物流企业,"安吉"品牌荣获上海市服务类现 代物流名牌称号。公司历年来多次获得上海大众、上海通用、上汽通用五菱、一汽丰田 、广汽丰田等客户授予的最佳供应商等奖项。 一、宗旨目标 随着安吉物流业务的蓬勃发展,原有的物流系统已经逐渐感到乏力,为此必须建立一 套适合安吉公司的物流优化体系,解决目前的窘境。本方案提出,以安吉物流管理信息 系统为主体,以信息流为支撑。通过资源调度平台、运输线路优化与运输方式调整,主 动持续配送服务的沟通联系,展开物流设计与运作,充分发挥资源调度平台的"全程掌控 "作用。 方案的宗旨:提高安吉物流的物流服务效率,总体水平上提高客户满意度,并保证相当 程度的物流成本。 方案的目标:优化原有运输配送线路,形成一套卓有成效的资源调度模式,提出更有效 的组织架构,使方案的整体更具有表现力。 二、物流模式 2.1、需求分析 安吉物流作为一家为汽车及零部件制造企业提供服务的第三方物流公司,下属业务包 括整车物流、零部件物流、口岸物流等三大业务板块,客户包括上海大众、上海通用、 上汽通用五菱、一汽丰田、广汽丰田、比亚迪等几乎国内所有主机厂。根据2000年到20 08年的整车物流运量图标(图2),得到平均需求增长率为230千辆/每年。而2011年安吉 物流的汽车物流量达574万辆,根据2000年到2008年的速率预测,2012年的汽车物流量将 达到600万辆。安吉物流正以快速的业务增长量急速发张。 图2(单位/千辆) 2.2、问题分析 随着汽车物流的发展,业务量的增大,各种问题逐渐突出。可以分为以下几点: 第一:汽车产业生产布局变大变宽的趋势在为汽车物流行业带来新机会的同时,也带 来了挑战和烦恼。尽管2011年汽车市场产销量变化不大,但汽车制造企业扩张 产生的布局却比以往更加分散,物流业务点随之明显增多,势必增加物流业务 的投入,加大物流服务的难度,增加物流企业的管理和运营成本,使得汽车物 流企业在需求和能力、投入和产出等方面都面临新的挑战和压力 第二:汽车物流业大量的用地需求难以得到满足。 第三:国内政策以及轿运车尚未标准化问题。 第四:铁路、水路由于诸多因素,制约了品质改善。安吉物流拥有充足的轿运车资源 ,并且公路运力的临时采购相对灵活快捷,因此在公路运力方面可以说没有任 何限制。而铁路、水路方面由于受列车班次、船期的限制,存在着年度最大运 能的限制。 第五:资源利用率低,运输成本高,运力资源浪费,竞争力薄弱。汽车物流行业的进 入门槛不高,造成目前行业群体数量多,单个企业控制车辆规模少。 第六:我国的现代物流起步较晚,导致国内物流人才的匮乏与产业高速增长不协调。 2.3总体物流模式 国内整车物流行业普遍遵循"两级分拨发运"体系。即:各生产基地的成品整车由整车分 拨中心(Vehicle Distribution Center,VDC)运至各整车仓储中心(Vehicle Storage Center,

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值