设为首页 加入收藏

TOP

改善代码可测性的若干技巧(二)
2018-01-11 06:06:47 】 浏览:590
Tags:改善 代码 若干 技巧
及lambda表达式隔离和模拟外部依赖更容易滴单测” 一文已经初步探讨了如何使用函数接口及lambda表达式来隔离和模拟外部依赖,增强代码可测性。不过不彻底。 如果一个方法里含有多个外部服务调用怎么办? 如果方法A调用B,B调用C,C调用D,D依赖了外部服务,怎么让 A,B,C,D更加容易测试? 如何可配置化地调用外部服务,而让类的大部分方法保持函数纯粹性而容易单测,少部分方法则承担外部服务调用的职责?指导思想是: 通过函数接口隔离外部服务依赖,分离出真正可单测的部分 。真正可单测的部分往往是条件性、循环性的不含服务调用依赖的业务性逻辑,而顺序的含服务调用依赖的流程性逻辑,应当通过接口测试用例来验证。

表达与执行分离

表达通常是声明式的,无状态的;执行通常是命令式的,有状态且依赖外部环境的。 表达与执行分离,可将状态与依赖分离出来,从而对表达本身进行单测。来看一段代码:

public Deliverer getDeliverInstance(DeliveryContext deliveryContext, ExpressParam params) {

    if (periodDeliverCondtion1) {
      LogUtils.info(log, "periodDeliverer for {}", params);
      return (Deliverer) applicationContext.getBean("periodDeliverer");
    }

    if(periodDeliverCondtion2){
      LogUtils.info(log, "periodDeliverer for {}", params);
      return (Deliverer) applicationContext.getBean("periodDeliverer");
    }

    if (fenxiaoDelivererCondition) {
      LogUtils.info(log, "fenxiaoDeliverer for {}", params);
      return (Deliverer) applicationContext.getBean("fenxiaoDeliverer");
    }
    if (giftDelivererCondition) {
      LogUtils.info(log, "giftDeliverer for {}", params);
      return (Deliverer) applicationContext.getBean("giftDeliverer");
    }
    if (localDelivererCondition) {
      LogUtils.info(log, "localDeliverr for {}", JsonUtils.toJson(order));
      return (Deliverer) applicationContext.getBean("localDeliverer");
    }
    LogUtils.info(log, "normalDeliverer for {}", params);
    return (Deliverer) applicationContext.getBean("normalDeliverer");
  }

这段代码根据不同条件,获取对应的发货子组件。 可见,代码要完成两个子功能: (1) 根据不同条件判断需要何种组件; (2) 获取相应组件,并打印必要日志。 (1) 是表达,真正值得测试的部分, (2) 是执行,通过接口测试即可验证; 而代码将(1)与(2) 混杂到一起,从而使得编写整个单测难度变大了,因为要mock applicationContext,还需要注入外部变量 log 。 可以将(1) 抽离出来,只返回要发货组件标识,更容易单测,而(2) 则使用多种方式实现。如下代码所示:

 public Deliverer getDeliverInstanceBetter(DeliveryContext deliveryContext, ExpressParam params) {
    return getActualDeliverInstance(getDeliverComponentID(deliveryContext, params).name(), params);
  }

  public DelivererEnum getDeliverComponentID(DeliveryContext deliveryContext, ExpressParam params) {

    if (periodDeliverCondtion1) {
      return periodDeliverer;
    }

    if(periodDeliverCondtion2){
      return periodDeliverer;
    }

    if (fenxiaoDelivererCondition) {
      return fenxiaoDeliverer;
    }
    if (giftDelivererCondition) {
      return giftDeliverer;
    }
    if (localDelivererCondition) {
      return localDeliverer;
    }
    return normalDeliverer;
  }

  public Deliverer getActualDeliverInstance(String componentName, ExpressParam params) {
    LogUtils.info(log, "component {} for {}", componentName, params);
    return (Deliverer) applicationContext.getBean(componentName);
  }

  public enum DelivererEnum {
    normal

虽然多出了两个方法,但是只有 getDeliverComponentID 方法是最核心的最需要单测的,并且是无状态不依赖外部环境的,很容易编写单测,只需要测试各种条件即可。这里定义了 DelivererEnum ,是为了规范发货组件的名称仅限于指定的若干种,防止拼写错误。

识别业务逻辑中的表达与执行,将表达部分分离出来。

分离纯函数

看下面这段代码:

/**
     * 根据指定rowkey列表及指定列族、列集合获取Hbase数据
     * @param tableName hbase表名
     * @param rowKeyList rowkey列表
     * @param cfName 列族
     * @param columns 列名
     * @param allowNull 是否允许值为null,通常针对rowkey
     * @return hbase 数据集
     * @throws Exception 获取数据集失败时抛出异常
     */
    public List<Result> getRows(Stri
首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Java Proxy 和 CGLIB 动态代理原理 下一篇200 行 Java 代码搞定计算器程序

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目