单元测试 HBase 应用程序

本章讨论使用 JUnit,Mockito,MRUnit 和 HBaseTestingUtility 对 HBase 应用程序进行单元测试。大部分信息来自关于测试 HBase 应用程序的社区博客文章。有关 HBase 本身的单元测试的信息,请参阅 hbase.tests

175. JUnit

HBase 使用 JUnit 进行单元测试

此示例将单元测试添加到以下示例类:

  1. public class MyHBaseDAO {
  2. public static void insertRecord(Table.getTable(table), HBaseTestObj obj)
  3. throws Exception {
  4. Put put = createPut(obj);
  5. table.put(put);
  6. }
  7. private static Put createPut(HBaseTestObj obj) {
  8. Put put = new Put(Bytes.toBytes(obj.getRowKey()));
  9. put.add(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1"),
  10. Bytes.toBytes(obj.getData1()));
  11. put.add(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2"),
  12. Bytes.toBytes(obj.getData2()));
  13. return put;
  14. }
  15. }

第一步是将 JUnit 依赖项添加到 Maven POM 文件中:

  1. <dependency>
  2. <groupId>junit</groupId>
  3. <artifactId>junit</artifactId>
  4. <version>4.11</version>
  5. <scope>test</scope>
  6. </dependency>

接下来,在代码中添加一些单元测试。测试用@Test注释。这里,单元测试以粗体显示。

  1. public class TestMyHbaseDAOData {
  2. @Test
  3. public void testCreatePut() throws Exception {
  4. HBaseTestObj obj = new HBaseTestObj();
  5. obj.setRowKey("ROWKEY-1");
  6. obj.setData1("DATA-1");
  7. obj.setData2("DATA-2");
  8. Put put = MyHBaseDAO.createPut(obj);
  9. assertEquals(obj.getRowKey(), Bytes.toString(put.getRow()));
  10. assertEquals(obj.getData1(), Bytes.toString(put.get(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1")).get(0).getValue()));
  11. assertEquals(obj.getData2(), Bytes.toString(put.get(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2")).get(0).getValue()));
  12. }
  13. }

这些测试确保您的createPut方法创建,填充和返回具有预期值的Put对象。当然,JUnit 可以做的远不止这些。有关 JUnit 的介绍,请参阅 https://github.com/junit-team/junit/wiki/Getting-started

176. Mockito

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 文件中。

  1. <dependency>
  2. <groupId>org.mockito</groupId>
  3. <artifactId>mockito-core</artifactId>
  4. <version>2.1.0</version>
  5. <scope>test</scope>
  6. </dependency>

接下来,将@RunWith注释添加到测试类,以指示它使用 Mockito。

  1. @RunWith(MockitoJUnitRunner.class)
  2. public class TestMyHBaseDAO{
  3. @Mock
  4. Configuration config = HBaseConfiguration.create();
  5. @Mock
  6. Connection connection = ConnectionFactory.createConnection(config);
  7. @Mock
  8. private Table table;
  9. @Captor
  10. private ArgumentCaptor putCaptor;
  11. @Test
  12. public void testInsertRecord() throws Exception {
  13. //return mock table when getTable is called
  14. when(connection.getTable(TableName.valueOf("tablename")).thenReturn(table);
  15. //create test object and make a call to the DAO that needs testing
  16. HBaseTestObj obj = new HBaseTestObj();
  17. obj.setRowKey("ROWKEY-1");
  18. obj.setData1("DATA-1");
  19. obj.setData2("DATA-2");
  20. MyHBaseDAO.insertRecord(table, obj);
  21. verify(table).put(putCaptor.capture());
  22. Put put = putCaptor.getValue();
  23. assertEquals(Bytes.toString(put.getRow()), obj.getRowKey());
  24. assert(put.has(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1")));
  25. assert(put.has(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2")));
  26. assertEquals(Bytes.toString(put.get(Bytes.toBytes("CF"),Bytes.toBytes("CQ-1")).get(0).getValue()), "DATA-1");
  27. assertEquals(Bytes.toString(put.get(Bytes.toBytes("CF"),Bytes.toBytes("CQ-2")).get(0).getValue()), "DATA-2");
  28. }
  29. }

该代码用ROWKEY-1'', DATA-1’’,`DATA-2''填充HBaseTestObj`作为值。然后它将记录插入到模拟表中。捕获 DAO 将插入的 Put,并测试值以验证它们是否符合您的预期。

这里的关键是在 DAO 之外管理 Connection 和 Table 实例创建。这允许您干净地模拟它们并测试 Puts,如上所示。同样,您现在可以扩展到其他操作,例如“获取”,“扫描”或“删除”。

177. MRUnit

Apache MRUnit 是一个允许您对 MapReduce 作业进行单元测试的库。您可以使用它以与其他 MapReduce 作业相同的方式测试 HBase 作业。

给定一个写入名为MyTest的 HBase 表的 MapReduce 作业,该表有一个名为CF的列族,这样的作业的缩减器可能如下所示:

  1. public class MyReducer extends TableReducer<Text, Text, ImmutableBytesWritable> {
  2. public static final byte[] CF = "CF".getBytes();
  3. public static final byte[] QUALIFIER = "CQ-1".getBytes();
  4. public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
  5. //bunch of processing to extract data to be inserted, in our case, let's say we are simply
  6. //appending all the records we receive from the mapper for this particular
  7. //key and insert one record into HBase
  8. StringBuffer data = new StringBuffer();
  9. Put put = new Put(Bytes.toBytes(key.toString()));
  10. for (Text val : values) {
  11. data = data.append(val);
  12. }
  13. put.add(CF, QUALIFIER, Bytes.toBytes(data.toString()));
  14. //write to HBase
  15. context.write(new ImmutableBytesWritable(Bytes.toBytes(key.toString())), put);
  16. }
  17. }

要测试此代码,第一步是将 MRUnit 的依赖项添加到 Maven POM 文件中。

  1. <dependency>
  2. <groupId>org.apache.mrunit</groupId>
  3. <artifactId>mrunit</artifactId>
  4. <version>1.0.0 </version>
  5. <scope>test</scope>
  6. </dependency>

接下来,在 Reducer 作业中使用 MRUnit 提供的 ReducerDriver。

  1. public class MyReducerTest {
  2. ReduceDriver<Text, Text, ImmutableBytesWritable, Writable> reduceDriver;
  3. byte[] CF = "CF".getBytes();
  4. byte[] QUALIFIER = "CQ-1".getBytes();
  5. @Before
  6. public void setUp() {
  7. MyReducer reducer = new MyReducer();
  8. reduceDriver = ReduceDriver.newReduceDriver(reducer);
  9. }
  10. @Test
  11. public void testHBaseInsert() throws IOException {
  12. String strKey = "RowKey-1", strValue = "DATA", strValue1 = "DATA1",
  13. strValue2 = "DATA2";
  14. List<Text> list = new ArrayList<Text>();
  15. list.add(new Text(strValue));
  16. list.add(new Text(strValue1));
  17. list.add(new Text(strValue2));
  18. //since in our case all that the reducer is doing is appending the records that the mapper
  19. //sends it, we should get the following back
  20. String expectedOutput = strValue + strValue1 + strValue2;
  21. //Setup Input, mimic what mapper would have passed
  22. //to the reducer and run test
  23. reduceDriver.withInput(new Text(strKey), list);
  24. //run the reducer and get its output
  25. List<Pair<ImmutableBytesWritable, Writable>> result = reduceDriver.run();
  26. //extract key from result and verify
  27. assertEquals(Bytes.toString(result.get(0).getFirst().get()), strKey);
  28. //extract value for CF/QUALIFIER and verify
  29. Put a = (Put)result.get(0).getSecond();
  30. String c = Bytes.toString(a.get(CF, QUALIFIER).get(0).getValue());
  31. assertEquals(expectedOutput,c );
  32. }
  33. }

您的 MRUnit 测试验证输出是否符合预期,插入 HBase 的 Put 具有正确的值,ColumnFamily 和 ColumnQualifier 具有正确的值。

MRUnit 包含一个 MapperDriver 来测试映射作业,您可以使用 MRUnit 测试其他操作,包括从 HBase 读取,处理数据或写入 HDFS,

178.使用 HBase Mini-Cluster 进行集成测试

HBase 附带 HBaseTestingUtility,这使得使用 迷你集群 编写集成测试变得容易。第一步是向 Maven POM 文件添加一些依赖项。检查版本以确保它们合适。

  1. <properties>
  2. <hbase.version>2.0.0-SNAPSHOT</hbase.version>
  3. </properties>
  4. <dependencies>
  5. <dependency>
  6. <groupId>org.apache.hbase</groupId>
  7. <artifactId>hbase-testing-util</artifactId>
  8. <version>${hbase.version}</version>
  9. <scope>test</scope>
  10. </dependency>
  11. </dependencies>

此代码表示 unit.tests 中显示的 MyDAO 插入的集成测试。

  1. public class MyHBaseIntegrationTest {
  2. private static HBaseTestingUtility utility;
  3. byte[] CF = "CF".getBytes();
  4. byte[] CQ1 = "CQ-1".getBytes();
  5. byte[] CQ2 = "CQ-2".getBytes();
  6. @Before
  7. public void setup() throws Exception {
  8. utility = new HBaseTestingUtility();
  9. utility.startMiniCluster();
  10. }
  11. @Test
  12. public void testInsert() throws Exception {
  13. Table table = utility.createTable(Bytes.toBytes("MyTest"), CF);
  14. HBaseTestObj obj = new HBaseTestObj();
  15. obj.setRowKey("ROWKEY-1");
  16. obj.setData1("DATA-1");
  17. obj.setData2("DATA-2");
  18. MyHBaseDAO.insertRecord(table, obj);
  19. Get get1 = new Get(Bytes.toBytes(obj.getRowKey()));
  20. get1.addColumn(CF, CQ1);
  21. Result result1 = table.get(get1);
  22. assertEquals(Bytes.toString(result1.getRow()), obj.getRowKey());
  23. assertEquals(Bytes.toString(result1.value()), obj.getData1());
  24. Get get2 = new Get(Bytes.toBytes(obj.getRowKey()));
  25. get2.addColumn(CF, CQ2);
  26. Result result2 = table.get(get2);
  27. assertEquals(Bytes.toString(result2.getRow()), obj.getRowKey());
  28. assertEquals(Bytes.toString(result2.value()), obj.getData2());
  29. }
  30. }

此代码创建一个 HBase 迷你集群并启动它。接下来,它创建一个名为MyTest的表,其中包含一个列族CF。插入记录,从同一个表执行 Get,并验证插入。

启动迷你集群大约需要 20-30 秒,但这应该适合集成测试。

有关 HBaseTestingUtility 的更多信息,请参阅 HBase 案例研究中的论文:使用 HBaseTestingUtility 进行本地测试和开发(2010)。