Welcome

首页 / 软件开发 / 数据结构与算法 / 使用MRUnit,Mockito和PowerMock进行Hadoop MapReduce作业的单元测试

使用MRUnit,Mockito和PowerMock进行Hadoop MapReduce作业的单元测试2013-11-17 infoq 奚亚伟译引言

Hadoop MapReduce作业有着独一无二的代码架构,这种代码架构拥有特定的模板和结构。这样 的架构会给测试驱动开发和单元测试带来一些麻烦。这篇文章是运用MRUnit,Mockito和PowerMock的真实范例 。我会介绍

使用MRUnit来编写Hadoop MapReduce应用程序的JUnit测试

使用PowerMock和Mockito模拟静态方法

模拟其他类型中的业务逻辑(译注:也就是编写测试驱动模块)

查看模拟的业务逻辑是否被调用(译注:测试驱动模块是否运行正常)

计数器

测试用例与log4j的集成

异常处理

本文的前提是读者应该已经熟悉JUnit 4的使用。

使用MRUnit可以把测试桩输入到mapper和/或 reducer中,然后在JUnit环境中判断是否通过测试。这个过程和任何JUnit测试一样,你可以调试你的代码。 MRUnit中的MapReduce Driver可以测试一组Map/Reduce或者Combiner。 PipelineMapReduceDriver可以测试 Map/Reduce作业工作流。目前,MRUnit还没有Partitioner对应的驱动。MRUnit使开发人员在面对Hadoop特殊 的架构的时候也能进行TDD和轻量级的单元测试。

实例

下面的例子中,我们会处理一些用来构 建地图的路面数据。输入的数据包括线性表面(表示道路)和交叉点(表示十字路口)。Mapper会处理每条路 面数据并把它们写入HDFS文件系统,并舍弃诸如十字路口之类的非线性路面数据。我们还会统计并打印所有输 入的非路面数据的数量。为了调试方便,我们也会额外打印路面数据的数量。

public class MergeAndSplineMapper extends Mapper<LongWritable, BytesWritable, LongWritable, BytesWritable> { private static Logger LOG = Logger.getLogger(MergeAndSplineMapper.class); enum SurfaceCounters { ROADS, NONLINEARS, UNKNOWN }@Override public void map(LongWritable key, BytesWritable value, Context context) throws IOException, InterruptedException {// A list of mixed surface typesLinkSurfaceMap lsm = (LinkSurfaceMap) BytesConverter.bytesToObject(value.getBytes());List<RoadSurface> mixedSurfaces = lsm.toSurfaceList();for (RoadSurface surface : mixedSurfaces){ Long surfaceId = surface.getNumericId(); Enums.SurfaceType surfaceType = surface.getSurfaceType(); if ( surfaceType.equals(SurfaceType.INTERSECTION)){ // Ignore non-linear surfaces. context.getCounter(SurfaceCounters.NONLINEARS).increment(1); continue; } else if ( ! surfaceType.equals(SurfaceType.ROAD) ) {// Ignore anything that wasn’t an INTERSECTION or ROAD, ie any future additions.context.getCounter(SurfaceCounters.UNKNOWN).increment(1);continue; } PopulatorPreprocessor.processLinearSurface(surface); // Write out the processed linear surface. lsm.setSurface(surface); context.write(new LongWritable(surfaceId), new BytesWritable(BytesConverter.objectToBytes(lsm))); if (LOG.isDebugEnabled()) { context.getCounter(SurfaceCounters.ROADS).increment(1); }} }}