本章讨论使用JUnit,Mockito,MRUnit和HBaseTestingUtility对HBase应用程序进行单元测试。大部分信息来自关于测试HBase应用程序的社区博客文章。有关HBase本身的单元测试的信息,请参阅 hbase.tests 。
HBase使用 JUnit 进行单元测试
此示例将单元测试添加到以下示例类:
public class MyHBaseDAO {
public static void insertRecord(Table.getTable(table), HBaseTestObj obj)
throws Exception {
Put put = createPut(obj);
table.put(put);
}
private static Put createPut(HBaseTestObj obj) {
Put put = new Put(Bytes.toBytes(obj.getRowKey()));
put.add(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1"),
Bytes.toBytes(obj.getData1()));
put.add(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2"),
Bytes.toBytes(obj.getData2()));
return put;
}
}
第一步是将JUnit依赖项添加到Maven POM文件中:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
接下来,在代码中添加一些单元测试。测试用@Test注释。这里,单元测试以粗体显示。
public class TestMyHbaseDAOData {
@Test
public void testCreatePut() throws Exception {
HBaseTestObj obj = new HBaseTestObj();
obj.setRowKey("ROWKEY-1");
obj.setData1("DATA-1");
obj.setData2("DATA-2");
Put put = MyHBaseDAO.createPut(obj);
assertEquals(obj.getRowKey(), Bytes.toString(put.getRow()));
assertEquals(obj.getData1(), Bytes.toString(put.get(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1")).get(0).getValue()));
assertEquals(obj.getData2(), Bytes.toString(put.get(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2")).get(0).getValue()));
}
}
这些测试确保您的createPut方法创建,填充和返回具有预期值的Put对象。当然,JUnit可以做的远不止这些。有关JUnit的介绍,请参阅 https://github.com/junit-team/junit/wiki/Getting-started 。
Mockito是一个嘲弄的框架。它比JUnit更进一步,允许您测试对象之间的交互,而不必复制整个环境。您可以在其项目网站上阅读有关Mockito的更多信息, https://code.google.com/p/mockito/ 。
您可以使用Mockito在较小的单元上进行单元测试。例如,您可以模拟org.apache.hadoop.hbase.Server实例或org.apache.hadoop.hbase.master.MasterServices接口参考而不是完整的org.apache.hadoop.hbase.master.HMaster。
此示例基于 unit.tests 中的示例代码,以测试insertRecord方法。
首先,将Mockito的依赖项添加到Maven POM文件中。
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>
接下来,将@RunWith注释添加到测试类,以指示它使用Mockito。
@RunWith(MockitoJUnitRunner.class)
public class TestMyHBaseDAO{
@Mock
Configuration config = HBaseConfiguration.create();
@Mock
Connection connection = ConnectionFactory.createConnection(config);
@Mock
private Table table;
@Captor
private ArgumentCaptor putCaptor;
@Test
public void testInsertRecord() throws Exception {
//return mock table when getTable is called
when(connection.getTable(TableName.valueOf("tablename")).thenReturn(table);
//create test object and make a call to the DAO that needs testing
HBaseTestObj obj = new HBaseTestObj();
obj.setRowKey("ROWKEY-1");
obj.setData1("DATA-1");
obj.setData2("DATA-2");
MyHBaseDAO.insertRecord(table, obj);
verify(table).put(putCaptor.capture());
Put put = putCaptor.getValue();
assertEquals(Bytes.toString(put.getRow()), obj.getRowKey());
assert(put.has(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1")));
assert(put.has(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2")));
assertEquals(Bytes.toString(put.get(Bytes.toBytes("CF"),Bytes.toBytes("CQ-1")).get(0).getValue()), "DATA-1");
assertEquals(Bytes.toString(put.get(Bytes.toBytes("CF"),Bytes.toBytes("CQ-2")).get(0).getValue()), "DATA-2");
}
}
该代码用ROWKEY-1'', DATA-1'',``DATA-2''填充HBaseTestObj作为值。然后它将记录插入到模拟表中。捕获DAO将插入的Put,并测试值以验证它们是否符合您的预期。
这里的关键是在DAO之外管理Connection和Table实例创建。这允许您干净地模拟它们并测试Puts,如上所示。同样,您现在可以扩展到其他操作,例如“获取”,“扫描”或“删除”。
Apache MRUnit 是一个允许您对MapReduce作业进行单元测试的库。您可以使用它以与其他MapReduce作业相同的方式测试HBase作业。
给定一个写入名为MyTest的HBase表的MapReduce作业,该表有一个名为CF的列族,这样的作业的缩减器可能如下所示:
public class MyReducer extends TableReducer<Text, Text, ImmutableBytesWritable> {
public static final byte[] CF = "CF".getBytes();
public static final byte[] QUALIFIER = "CQ-1".getBytes();
public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
//bunch of processing to extract data to be inserted, in our case, let's say we are simply
//appending all the records we receive from the mapper for this particular
//key and insert one record into HBase
StringBuffer data = new StringBuffer();
Put put = new Put(Bytes.toBytes(key.toString()));
for (Text val : values) {
data = data.append(val);
}
put.add(CF, QUALIFIER, Bytes.toBytes(data.toString()));
//write to HBase
context.write(new ImmutableBytesWritable(Bytes.toBytes(key.toString())), put);
}
}
要测试此代码,第一步是将MRUnit的依赖项添加到Maven POM文件中。
<dependency>
<groupId>org.apache.mrunit</groupId>
<artifactId>mrunit</artifactId>
<version>1.0.0 </version>
<scope>test</scope>
</dependency>
接下来,在Reducer作业中使用MRUnit提供的ReducerDriver。
public class MyReducerTest {
ReduceDriver<Text, Text, ImmutableBytesWritable, Writable> reduceDriver;
byte[] CF = "CF".getBytes();
byte[] QUALIFIER = "CQ-1".getBytes();
@Before
public void setUp() {
MyReducer reducer = new MyReducer();
reduceDriver = ReduceDriver.newReduceDriver(reducer);
}
@Test
public void testHBaseInsert() throws IOException {
String strKey = "RowKey-1", strValue = "DATA", strValue1 = "DATA1",
strValue2 = "DATA2";
List<Text> list = new ArrayList<Text>();
list.add(new Text(strValue));
list.add(new Text(strValue1));
list.add(new Text(strValue2));
//since in our case all that the reducer is doing is appending the records that the mapper
//sends it, we should get the following back
String expectedOutput = strValue + strValue1 + strValue2;
//Setup Input, mimic what mapper would have passed
//to the reducer and run test
reduceDriver.withInput(new Text(strKey), list);
//run the reducer and get its output
List<Pair<ImmutableBytesWritable, Writable>> result = reduceDriver.run();
//extract key from result and verify
assertEquals(Bytes.toString(result.get(0).getFirst().get()), strKey);
//extract value for CF/QUALIFIER and verify
Put a = (Put)result.get(0).getSecond();
String c = Bytes.toString(a.get(CF, QUALIFIER).get(0).getValue());
assertEquals(expectedOutput,c );
}
}
您的MRUnit测试验证输出是否符合预期,插入HBase的Put具有正确的值,ColumnFamily和ColumnQualifier具有正确的值。
MRUnit包含一个MapperDriver来测试映射作业,您可以使用MRUnit测试其他操作,包括从HBase读取,处理数据或写入HDFS,
HBase附带HBaseTestingUtility,这使得使用_迷你集群_编写集成测试变得容易。第一步是向Maven POM文件添加一些依赖项。检查版本以确保它们合适。
<properties>
<hbase.version>2.0.0-SNAPSHOT</hbase.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-testing-util</artifactId>
<version>${hbase.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
此代码表示 unit.tests 中显示的MyDAO插入的集成测试。
public class MyHBaseIntegrationTest {
private static HBaseTestingUtility utility;
byte[] CF = "CF".getBytes();
byte[] CQ1 = "CQ-1".getBytes();
byte[] CQ2 = "CQ-2".getBytes();
@Before
public void setup() throws Exception {
utility = new HBaseTestingUtility();
utility.startMiniCluster();
}
@Test
public void testInsert() throws Exception {
Table table = utility.createTable(Bytes.toBytes("MyTest"), CF);
HBaseTestObj obj = new HBaseTestObj();
obj.setRowKey("ROWKEY-1");
obj.setData1("DATA-1");
obj.setData2("DATA-2");
MyHBaseDAO.insertRecord(table, obj);
Get get1 = new Get(Bytes.toBytes(obj.getRowKey()));
get1.addColumn(CF, CQ1);
Result result1 = table.get(get1);
assertEquals(Bytes.toString(result1.getRow()), obj.getRowKey());
assertEquals(Bytes.toString(result1.value()), obj.getData1());
Get get2 = new Get(Bytes.toBytes(obj.getRowKey()));
get2.addColumn(CF, CQ2);
Result result2 = table.get(get2);
assertEquals(Bytes.toString(result2.getRow()), obj.getRowKey());
assertEquals(Bytes.toString(result2.value()), obj.getData2());
}
}
此代码创建一个HBase迷你集群并启动它。接下来,它创建一个名为MyTest的表,其中包含一个列族CF。插入记录,从同一个表执行Get,并验证插入。
启动迷你集群大约需要20-30秒,但这应该适合集成测试。
有关HBaseTestingUtility的更多信息,请参阅 HBase案例研究中的论文:使用HBaseTestingUtility进行本地测试和开发(2010)。