# 单元测试 HBase 应用程序

本章讨论使用 JUnit，Mockito，MRUnit 和 HBaseTestingUtility 对 HBase 应用程序进行单元测试。大部分信息来自[关于测试 HBase 应用程序的社区博客文章](http://blog.cloudera.com/blog/2013/09/how-to-test-hbase-applications-using-popular-tools/)。有关 HBase 本身的单元测试的信息，请参阅 [hbase.tests](#hbase.tests) 。

## 175\. JUnit

HBase 使用 [JUnit](http://junit.org) 进行单元测试

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

```
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](https://github.com/junit-team/junit/wiki/Getting-started) 。

## 176\. Mockito

Mockito 是一个嘲弄的框架。它比 JUnit 更进一步，允许您测试对象之间的交互，而不必复制整个环境。您可以在其项目网站上阅读有关 Mockito 的更多信息， [https://code.google.com/p/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](#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，如上所示。同样，您现在可以扩展到其他操作，例如“获取”，“扫描”或“删除”。

## 177\. MRUnit

[Apache MRUnit](https://mrunit.apache.org/) 是一个允许您对 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，

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

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](#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 进行本地测试和开发](http://blog.sematext.com/2010/08/30/hbase-case-study-using-hbasetestingutility-for-local-testing-development/)（2010）。