MapReduce程序的测试--MRUnit
实在没什么空,好长时间没有写Blog,心虚,没有去参加校园招聘的人伤不起~
MRUnit是一个开源的MapReduce程序测试框架,使用MRUnit,可以在单元测试的时候不用每次都上集群跑一遍,提高测试效率。
MRUnit官网:http://mrunit.apache.org/
注意,当你第一次使用的时候,发现出现问题解决不了,看看换个版本能行不[它现在有hadoop1、hadoop2两个版本,个人认为Turtorial上写错了]。
MRUnit是基于JUnit和Mockito的,你还需要把Mockito的包加入路径中来:http://code.google.com/p/mockito/downloads/list
MRUnit 0.9.0的代码量很少,总共才25个左右的文件,220K大小。
如何使用MRUnit进行MapReduce程序的测试请参看Turtorial:https://cwiki.apache.org/confluence/display/MRUNIT/MRUnit+Tutorial
下面是源码目录结构:
换电脑没工具,类图待补:
主要就是外面这些主要就是外面这些文件和mapreduce目录[New API]。
TestDriver:TestDriver是所有测试驱动类的父类, 如果你看了上面的Turtorial,就知道expectedOutputs是存放你期望输出的结果的,这里的结果会用来和你的测试输入用例运行结果比较,如果一致则测试通过,否则显然测试不通过;configuration是配置,其它是Counter相关的。
TestDriver最重要的方法是validate(outputs,orderMatters)方法,这个方法是用来验证输出是否与期望输出相同的。outputs是执行测试数据后实际输出,将被用来和expectedOutputs比较,orderMatters决定在比较的时候是否要严格按照出现顺序。
另外还有一个特别重要的方法是run()方法,这是执行这个Driver的方法。比如MapDriver执行run来对输入数据运行map函数得到输出。
而runTest()首先执行run()方法,然后将实际输出与期望输出进行比较[validate]。
MapDriverBase:
MapDriverBase是MapDriver的父类,inputKey,inputVal是输入的K-V对,这是一个不好的设计,因为这样我们只能测试一个K-V对输入。
按照上面说的,先执行run,然后再验证期望输出与实际输出是否一致,再验证相关Counters.
这个是我们实际用来测试mapper的类,它包含了一个Mapper,对应我们编写的mapper,然后Counters就是hadoop里的Counters,收集各种计数。
我们看到它的原理很简单,就是调用map函数去处理K-V对,然后收集结果和Counters起来。
继承与TestDriver,inputKey是输入的Key,inputValues对应reduce函数中的values,很显然,同样只能测试一个输入用例。
它的runTest()的作用于MapDriverBase的runTest()作用相同。
与MapDriver同理,myReducer对应我们编写的Reducer。
而run()方法也是调用reduce()方法,然后收集结果。
这个是测试整个MapReduce程序的MapReduceDriver的父类。
可以看到,inputList是输入K-V对,另外两个是比较器,在模拟shuffle的时候对key排序和group内排序什么的。
跟其他XXXBase一样,runTest()首先是调用run,然后验证结果。
shuffle()函数就是将map的输出排排序,然后将K-V对收集成reduce的输出K-list(V)
整个MapReduce程序一般都有的mapper,reducer,combiner。
执行reduce过程无非就是把K-List(V1)给reduce函数执行一下咯,然后就可以收集结果啦,实际是调用ReduceDriver的run方法。
整个MapReduceDriver的run方法,则是先用MapDriver执行一下,然后ReduceDriver执行一下,如果有Combiner则也安装ReduceDriver的方式执行。
然后就得到reduce端的输出结果啦,然后到MapReduceDriverBase.runTest()一验证,Ok!
我们看到MRUnit的原理其实是很简单的,但仅仅是MRUnit能做的东西也很有限,如果要支持更多的功能,比如一次测试多个用例,获取分片等等,一般有两个方法,一个是写一些工具类,利用Mock技术辅助进行测试,另一个则是自己改写MRUnit,个人认为第二种方式比较好。