在描述的情况下寻找可测试的设计

时间:2014-02-26 08:15:12

标签: unit-testing design-patterns testing

我有一个系统,它以某种ABC格式从外部系统获取对象列表,将其转换为内部表示并传递给外部服务:

class ABCService() {
  public ABCService(ExtService extService) {
    this.extService = extService;
  }

  public void do(ABCData [] abcObjs) throws NoDataException {
    if (abcObjs.length == 0) {
      throw NoDataException();
    } else {
      List<Data> objs = new ArrayList<>();

      for (ABCData abcObj : abcObjs) {
        Data obj = Parser.parse(abcObj); // static call
        objs.add(obj);
      }

      extService.do(objs);
    }
  }
}

在测试ABCService时,我们可以测试两件事:

  • 如果没有数据传递给“do”,则服务会抛出异常;
  • 如果某些数据传递给“do”,则服务应该调用extService并传递完全相同数量的对象,它从测试调用者那里收到。

但是,虽然Parser工厂也经过测试,但无法保证输出“objs”数组以某种方式连接到输入abcObjs(例如,方法已创建具有预定义长度的列表,但方法“忘记”填充列表)

我认为这两个测试用例并没有完全覆盖方法的工作流程,导致其中一些测试用例未经测试。

如何修改ABCService设计以提高其可测试性?

2 个答案:

答案 0 :(得分:1)

此代码中的主要测试难点是您有两个协作者,其中一个是静态的。

如果您可以将Parser转换为非静态(或者可能将其包装在非静态中)并在执行extService时注入,则可以测试是否调用了解析器使用正确的参数进行正确的次数。在解析器的返回值中进行存根,您还可以验证是否使用适当转换的对象调用了extService,而不是仅使用正确数量的对象。

答案 1 :(得分:0)

您遇到的问题是尝试在一个函数中处理两个任务。函数 do 可以在逻辑上分成两个不同的成员函数,这样你就可以对每个函数使用unittest。

通过使用重构,您可以将解析和填充逻辑提取到另一个成员函数中。

class ABCService() {
  public void do(ABCData [] abcObjs) throws NoDataException {
    extService.do(populateList(abcObjs));
  }

  List<Data> popuateList(ABCData[] abcObjs) {
    if (abcObjs.length == 0) {
      throw NoDataException();
    } else {
      List<Data> objs = new ArrayList<>();
      for (ABCData abcObj : abcObjs) {
      Data obj = Parser.parse(abcObj); // static call
      objs.add(obj);
      return objs;
    }
  }      
}

虽然您当前的unittest仍然可以保留“do”功能,另外,您可以为“populateList”函数添加unittest案例以确保它生成正确的数据列表