单元测试如何保证了易用的API?

发表于:2019-3-20 10:53

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Ender    来源:思否

  一般而言TDD的好处是以输出为导向及早发现问题,以及方便重构(单元测试保证).
  我理解,还有一个比较重要的意义是: 客观上强制了程序员写出更加友好的接口 方便测试和联调.
  问题
  这里我以c++举例,需求就用最简单的: 实现一个单例类(比如说一个读取数据库的单例).
  好,拿到这个需求了,考虑到c++11之后static本身就是多线程安全的,所以实现一个单例模式就很简单了,如下:
   // 手打不保证编译ok
  class SingleDb {
  public:
  int getMoney(){...}
  SinlgeDb(const SingleDb &) = delete;
  void operator=(const SingleDb &) = delete;
  static SingleDb &get() {
  static SingleDb db;
  return db;
  }
  };
  ok, 单例类的主体工作基本上就完成了,代码中直接可以用SingleDb::get()就可以获得这个单例,再补充和业务相关的读取等成员函数即可.
  好了,本来这样就ok了,但是老板现在要求大家每个新功能都要求写单元测试,客户端程序员(这里的客户端指代的是使用这个单例模式的程序员)调用了这个API之后就不爽了,因为他想要对他自己的业务代码进行单元测试,但是在读数据库的时候无法进行打桩测试。具体遇到的问题如下:
   struct Client{
  int doSth() const {
  SingleDb &db = SingleDb::get();
  if(db.getMoney() < 0) return -1;
  }
  };
  完全无法测试,因为我们在写单元测试的时候无法控制db.getMoney()的输出进行控制. 因为需要做如下改造:
  API适配
  数据库的对象不通过静态成员函数获取,而修改成注入的方式,这样方便构造输入.
  数据库单例类没有抽象基类,无法用构造类(或者说是Mock对象)替换, 需改改写成有继承体系的类.
  实现:
  针对以上两点,修改之后的样子
   struct DbBase {
  virtual int getMoney() = 0;
  };
  class SingleDb: public DbBase {
  // 没啥变化
  };
  //客户端
  struct Client {
  DbBase &db;
  explicit Client(DbBase &db): db(db) {}
  int doSth() const {
  if(db.getMoney() < 0) return -1;
  }
  };
  使用(单元测试)
  这样处理之后,单例类已经变成了一个更加易于单元测试的类了,以一个比较简单的单元测试框架作为例子给出(catch+fakeit)
   TEST_CASE("", "")
  {
  using namespace fakeit;
  Mock mock;
  when(Method(mock, getMoney())).Return(-1);
  Client c(mock.get());
  REQUIRE(c.doSth() == -1);
  }
  这实际上是为了单元测试而把API的接口改了,不仅仅是更加易于单元测试了. 更有意义的是,把接口提升至抽象类,以后想扩展实现类,直接就新增继承类即可,单元测试都不用动(因为传入的是抽象基类).
  或者哪一天用到的单元测试框架没人维护了需要切换单元测试代码,那业务代码根本也不需要动,因为抽象接口已经固定. 不用Mock框架,自己打个桩都能单元测试, 例子如下:
   // 构造打桩继承类
  struct DataBaseMock : public DbBase
  int getMoney() {return -1;}
  };
  // 测试
  ...
  DataBaseMock db;
  Client c(&db);
  REQUIRE(c.doSth() == -1);
  总结:
  实际上,撰写一个好的API的好处本身又是另外一个话题了(不仅仅有助于单元测试),但是TDD这个开发模式能够强迫程序员写出一个更加易用的API.

      上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号