引子

  前一段时间,公直给大家培训Toast工具的使用,以及分享了单元测试的心得。其间提到的单元测试中经常用到的mock技术。这两天我尝试Mock技术在hadoop上的使用做个例子,分享一下我的理解,希望能给开发和测试同学带来一些帮助。

  为什么单元测试需要使用mock?我们先看看一个有意思的定义,根据《测试驱动开发的艺术》一书中的定义,在以下情况中一个测试不是单元测试:

  1、访问了数据

  2、有网络通讯

  3、访问了文件系统

  4、不与其他任何单元测试同时运行

  5、必须配置好环境后才能运行

  我们现实项目中常常遇到单元测试因为环境等各种外界因素频繁失效,因为依赖的类和方法变动频繁失效,检查和维护这些测试代码,让人非常烦躁。其实反过来看看上面的定义,我们会发现很多时候对单元测试的理解是有问题的,是与集成测试、系统测试简单划上了等号的。

  单元测试的目的不在于集成,而在于以简洁有效的方式,对代码的逻辑进行快速的验证,通过测试代码不断推动开发工作的进行。这很自然的需要使用mock技术来替换掉一些逻辑无关的依赖。尤其在多人协作开发的项目中,mock方法是提高项目效率的常见手段。

  回到正题,hadoop的单元测试一直是个挑战。在不间断的google中,目前为止也没有找到一个特别合适的单元测试框架。

  也因此我一直在思考hadoop之上的单元测试如何开展,在之前我的一封邮件中只是给出了个简单的注入式mock的手段。但实际上,开发hadoop之上的单元测试代码,确实挑战不小,有些挑战是hadoop带来的,有些是被测代码的设计带来的。

  这里我分享一下,使用Mockito(《hadoop权威指南》中mock工具包)+ PowerMock工具来做hadoop上一个头疼函数的单元测试。

  首先看看被测代码

  这次挑的是店铺搜索的ShopScanMap类:


public class ShopScanMap extends TableMapper<Text, Text> {
@Override
    public void setup(Context context) throws IOException {
          //…
       Configuration conf = context.getConfiguration();
 
       ht_shop_info = new HTable(conf, Bytes.toBytes(ParaDefine.t_shop_info));
       ht_bmw_users = new HTable(conf, Bytes.toBytes(ParaDefine.t_bmw_users));
      
         …
 
       count_bmw = context.getCounter("Statistics", ParaDefine.t_bmw_shops);
 
         //…
}
 
    @Override
    public void map(ImmutableBytesWritable inKey, Result inValue,
           Context context) throws IOException, InterruptedException {
         //调用main_process方法
}
 
private int main_process(Map<String, String> mapOutPut,
           Field_Bmw_Shops fBmwShops, long shop_id, TimeBill timeBill_shop,
           TimeBill timeBill_user) throws IOException {
         //…
       Get gtShopInfo = new Get(r_shopinfo.getBytes());
       Result rsShopInfo = ht_shop_info.get(gtShopInfo);
         //…
}
}