blob: 620b59d91c824f2c4a1762270276f7e24d4684cf [file] [log] [blame]
<body>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<p>
MRUnit is a library designed to allow easy testing of Mapper and Reducer
classes using existing tools such as JUnit. MRUnit provides mock
implementations of OutputCollector and Reporter for use in calling <tt>Mapper.map()</tt>
and <tt>Reducer.reduce()</tt>, as well as a set of "driver" classes that manage
delivery of key/value pair inputs to tasks, and comparison of actual task
outputs with expected outputs.
</p>
<p>
The primary advantage of MRUnit is that it allows you to test the outputs
of individual maps and reduces, as well as the composition of the two, without
needing to use the MiniMR cluster, or start a real MapReduce job in Hadoop,
which are time-consuming processes.
</p>
<h3>Using MRUnit</h3>
MRUnit is designed to allow you to write ordinary JUnit test suites.
<ul>
<li> Include lib/mrunit-0.1.jar and lib/junit-4.4.jar on the classpath when
building and testing.</li>
<li> The test methods for your Mapper implementation should use instances
of the <tt>MapDriver</tt>.</li>
<li> The test methods for your Reducer implementation should use instances
of the <tt>ReduceDriver</tt>.</li>
<li> MapReduce "jobs" consisting of a small number of inputs can be tested
with the <tt>MapReduceDriver</tt>. This supports a simple "shuffle" of outputs
to maintain the expected input delivery semantics to the reducer.</li>
</ul>
<p>
A <tt>MapDriver</tt> or <tt>ReduceDriver</tt> instance is created for each test,
as well as a fresh instance of your Mapper or Reducer. The driver is configured
with the input keys and values, and expected output keys and values.
</p>
<p>
The <tt>run()</tt> method will execute the map or reduce, and returns the outputs
retrieved from the OutputCollector.
The <tt>runTest()</tt> method will execute the map or reduce, and compares the
actual outputs with the expected outputs, and returns true to indicate
success and false on failure.
When expecting multiple outputs, the test drivers enforce that the order of
the actual outputs is the same as the order in which outputs are configured
(i.e., the order of calls to <tt>withOutput()</tt> or <tt>addOutput()</tt>).
</p>
<h3>Example</h3>
<p>
A brief test of Hadoop's <tt>IdentityMapper</tt> is presented here:
</p>
<div><tt><pre>
import junit.framework.TestCase;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.lib.IdentityMapper;
import org.junit.Before;
import org.junit.Test;
public class TestExample extends TestCase {
private Mapper<Text, Text, Text, Text> mapper;
private MapDriver<Text, Text, Text, Text> driver;
&#64;Before
public void setUp() {
mapper = new IdentityMapper<Text, Text>();
driver = new MapDriver<Text, Text, Text, Text>(mapper);
}
&#64;Test
public void testIdentityMapper() {
driver.withInput(new Text("foo"), new Text("bar"))
.withOutput(new Text("foo"), new Text("bar"))
.runTest();
}
}
</pre></tt></div>
<p>
This test first instantiates the Mapper and MapDriver. It configures
an input (key, value) pair consisting of the strings "foo" and "bar",
and expects these same values as output. It then calls <tt>runTest()</tt> to
actually invoke the mapper, and compare the actual and expected outputs.
The <tt>runTest()</tt> method will throw a RuntimeException if the output
is not what it expects, which causes JUnit to mark the test case as failed.
</p>
<p>All <tt>with*()</tt> methods in MRUnit return a reference to <tt>this</tt>
to allow them to be easily chained (e.g.,
<tt>driver.withInput(a, b).withOutput(c, d).withOutput(d, e)...</tt>). These
methods are analogous to the more conventional <tt>setInput()</tt>, <tt>addOutput()</tt>,
etc. methods, which are also included.</p>
<p>
Further examples of MRUnit usage can be seen in its own <tt>test/</tt> directory.
The above example is in <tt>org.apache.hadoop.mrunit.TestExample</tt>. Further
&quot;tests&quot; of the IdentityMapper are used to test the correctness of MRUnit
itself; <tt>org.apache.hadoop.mrunit.TestMapDriver</tt> includes several tests of
correctness for the MapDriver class; the <tt>testRunTest*()</tt> methods show
how to apply the MapDriver to the IdentityMapper to confirm behavior surrounding
both correct and incorrect input/output data sets. The <tt>testRunTest*()</tt> methods
in <tt>org.apache.hadoop.mrunit.TestReduceDriver</tt> show how to apply the ReduceDriver
test component to the LongSumReducer class.
</p>
</body>