当前位置: 首页 > 工具软件 > MRUnit > 使用案例 >

使用MRUnit测试Hadoop程序

慕俊迈
2023-12-01
这篇文章将略微绕开使用MapReduce实现数据密集型处理中的模式以讨论同样重要的测试。 汤姆•惠勒Tom Wheeler)在纽约2012年Strata / Hadoop World会议上参加的一次演讲给了我部分启发。 处理大型数据集时,想到的并不是单元测试。 但是,当考虑到无论集群有多大或有多少数据时,都将相同的代码推送到所有节点以运行MapReduce作业,因此Hadoop映射器和化简器非常适合进行单元测试。 但是,对框架进行Hadoop的单元测试并不容易。 幸运的是,有一个使Hadoop测试相当容易的库– MRUnit 。 MRUnit基于JUnit,并允许对映射器,化简器进行单元测试以及对映射器进行一些有限的集成测试-归化器与组合器,自定义计数器和分区器之间的交互。 在撰写本文时,我们正在使用MRUnit的最新版本0.9.0。 所有测试的代码均来自上一则有关使用本地聚合计算平均值的文章

设定

要开始使用,请从此处下载MRUnit。 解压缩tar文件后,将cd插入mrunit-0.9.0-incubating / lib目录。 在其中,您应该看到以下内容:

  1. mrunit-0.9.0-incubating-hadoop1.jar
  2. mrunit-0.9.0-incubating-hadoop2.jar

我敢肯定,mrunit-0.9.0-incubating-hadoop1.jar用于Hadoop的MapReduce版本1,而mrunit-0.9.0-incubating-hadoop2.jar用于处理Hadoop的新版本的MapReduce。 对于本文以及其他所有后续文章,我们将使用Cloudera CDH4.1.1发行版中的hadoop-2.0版本,因此我们需要mrunit-0.9.0-incubating-hadoop2.jar文件。 我在Intellij中将MRUnit,JUnit和Mockito添加为库(JUnit和Mockito与MRUnit jar文件位于同一目录中)。 现在我们已经建立了依赖关系,让我们开始测试。

测试映射器

设置测试映射器非常简单,最好先查看一些代码来说明。 我们将使用上一篇文章中的映射器内合并示例:

@Test
public void testCombiningMapper() throws Exception {
   new MapDriver<LongWritable,Text,Text,TemperatureAveragingPair>()
           .withMapper(new AverageTemperatureCombiningMapper())
           .withInput(new LongWritable(4),new Text(temps[3]))
           .withOutput(new Text('190101'),new TemperatureAveragingPair(-61,1))
           .runTest();
 }

注意流利的api样式,这增加了创建测试的便利性。 要编写测试,您将:

  1. 实例化完全与被测映射器参数化的MapDriver类的实例。
  2. 在withMapper调用中添加要测试的Mapper实例。
  3. 在withInput调用中,输入您的键和输入值,在这种情况下,一个LongWritable具有任意值和一个Text对象,该对象包含来自NCDC天气数据集的行,该数据集包含在名为“ temps”的字符串数组中,该数组早些时候在其中建立。测试(此处不会显示,因为它会从演示文稿中删除)。
  4. 在withOutput调用中指定期望的输出,这里我们期望一个Text对象的值为“ 190101”,一个TemperatureAveragingPair对象的值为-61(温度)和1(计数)。
  5. 最后一个调用runTest将指定的输入值输入到映射器中,并将实际输出与“ withOutput”方法中设置的预期输出进行比较。

要注意的一件事是MapDriver每次测试仅允许一个输入和输出。 您可以根据需要多次调用withInput和withOutput,但是MapDriver会用新值覆盖现有值,因此您将只能在任何时候使用一个输入/输出进行测试。 为了指定多个输入,我们将使用MapReduceDriver,稍后将介绍几节,但接下来将测试reducer。

测试减速器

测试减速器遵循与映射器测试相同的模式。 再次,让我们看一个代码示例:

@Test
public void testReducerCold(){
  List<TemperatureAveragingPair> pairList = new ArrayList<TemperatureAveragingPair>();
      pairList.add(new TemperatureAveragingPair(-78,1));
      pairList.add(new TemperatureAveragingPair(-84,1));
      pairList.add(new TemperatureAveragingPair(-28,1));
      pairList.add(new TemperatureAveragingPair(-56,1));

      new ReduceDriver<Text,TemperatureAveragingPair,Text,IntWritable>()
                .withReducer(new AverageTemperatureReducer())
                .withInput(new Text('190101'), pairList)
                .withOutput(new Text('190101'),new IntWritable(-61))
                .runTest();
    }
  1. 该测试首先创建一个TemperatureAveragingPair对象列表,用作减速器的输入。
  2. 实例化了ReducerDriver,并且与MapperDriver一样,对它的参数设置也与被测试的reducer完全相同。
  3. 接下来,我们要在withReducer调用中传入要测试的reducer实例。
  4. 在withInput调用中,我们传入键“ 190101”和在测试开始时创建的pairList对象。
  5. 接下来,我们指定我们期望减速器发出的输出,相同的键“ 190101”和一个IntWritable,它表示列表中的温度平均值。
  6. 最后调用runTest,它将给我们的减速器提供指定的输入,并将减速器的输出与期望输出进行比较。

ReducerDriver具有与MapperDriver相同的限制,即不接受多个输入/输出对。 到目前为止,我们已经单独测试了Mapper和Reducer,但我们也想在集成测试中一起测试它们。 可以通过使用MapReduceDriver类完成集成测试。 MapReduceDriver还是用于测试组合器,自定义计数器或自定义分区程序使用情况的类。

整合测试

为了测试您的mapper和reducer一起工作,MRUnit提供了MapReduceDriver类。 正如您现在所期望的,MapReduceDriver类有两个主要区别。 首先,参数化映射器的输入和输出类型以及化简器的输入和输出类型。 由于映射器输出类型需要与化简器输入类型匹配,因此最终需要3对参数化类型。 其次,您可以提供多个输入并指定多个预期输出。 这是我们的示例代码:

@Test
public void testMapReduce(){

new MapReduceDriver<LongWritable,Text,
                      Text,TemperatureAveragingPair,
                      Text,IntWritable>()
                .withMapper(new AverageTemperatureMapper())
                .withInput(new LongWritable(1),new Text(temps[0]))
                .withInput(new LongWritable(2),new Text(temps[1]))
                .withInput(new LongWritable(3),new Text(temps[2]))
                .withInput(new LongWritable(4),new Text(temps[3]))
                .withInput(new LongWritable(5),new Text(temps[6]))
                .withInput(new LongWritable(6),new Text(temps[7]))
                .withInput(new LongWritable(7),new Text(temps[8]))
                .withInput(new LongWritable(8),new Text(temps[9]))
                .withCombiner(new AverageTemperatureCombiner())
                .withReducer(new AverageTemperatureReducer())
                .withOutput(new Text('190101'),new IntWritable(-22))
                .withOutput(new Text('190102'),new IntWritable(-40))
                .runTest();
    }

从上面的示例中可以看到,设置与MapDriver和ReduceDriver类相同。 您传入了映射器,reducer和(可选)组合器的实例进行测试。 MapReduceDriver允许我们传递具有不同键的多个输入。 此处的“温度”数组与Mapper样本中引用的数组相同,并包含NCDC天气数据集中的几行内容,这些样本行中的关键字为1901年1月和2月,分别表示为“ 190101”和“ 190102“。 该测试是成功的,因此我们对映射器和化简器一起工作的正确性有了更多的信心。

结论

希望我们已经证明了MRUnit对于测试Hadoop程序有多么有用。 我想用我自己的一些观点来总结这篇文章。 尽管MRUnit使映射器和化简器代码的单元测试变得容易,但是这里介绍的映射器和化简器示例相当简单。 如果您的映射和/或精简代码开始变得更加复杂,则最好将代码与Hadoop框架解耦,然后单独测试新类。 另外,与MapReduceDriver类一样,它用于集成测试也非常有用,很容易达到不再测试代码,而已经测试Hadoop框架本身的地步。 我提出了自己打算继续使用的测试策略:

  1. 单元测试映射/减少代码。
  2. 可能使用MapReduceDriver类编写一个集成测试。
  3. 作为健全性检查,请在单节点安装(在我的笔记本电脑上)上运行MapReduce作业,以确保其在Hadoop框架上运行。
  4. 然后在我的案例中,使用Apache Whirr在EC2的测试集群上运行我的代码。

讨论如何在笔记本电脑(OSX Lion)上设置单节点安装以及如何使用Whirr在EC2上建立群集将使这篇文章过长,因此我将在下一篇文章中介绍这些主题。 谢谢你的时间。

资源资源


参考:来自我们的JCG合作伙伴 Bill Bejeck的《 Random Thoughts On Coding》博客中的MRUnit测试Hadoop程序

翻译自: https://www.javacodegeeks.com/2012/11/testing-hadoop-programs-with-mrunit.html

 类似资料: