您的位置:首页 > 其它

使用常用工具测试HBase应用

2014-02-25 06:48 543 查看
虽然业界采用Apache HBase来构建终端用户应用的范围越来越多,但是许多这种应用并没有经过良好的测试。通过这篇文章,你可以了解到有关这方面的一些容易实现的测试方法。

我们首先以JUnit为例, 然后是Mockito 和Apache
MRUnit, 接着会使用HBase的一个微型集群来做集成测试。(HBase自身的代码也是通过一个微型的集群来测试的, 所以对于上游的应用为什么不能这样测试呢?)

作为探讨的基础,我们假设你创建了用于将数据插入到HBase 的数据访问对象(DAO)。实际的逻辑可能很复杂,但为了演示用例,以下简单的代码也可以完成基本的功能:

[java] view
plaincopy

public class MyHBaseDAO {  

  

                public static void insertRecord(HTableInterface 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;  

                }  

}  

 

HBaseTestObj是一个含有成员rowkey, data1, and data2及其getter和setter的对象。

方法insertRecord向HBase表插入了列族为CF, 列为CQ-1,CQ-2的记录。方法createPut 简单的包装了Put数据并返回给调用者。


使用JUnit

对于大部分Java开发者来说都很熟悉的JUnit, 可以容易的应用到HBase大部分程序中。 首先,在pom中添加依赖库:

[java] view
plaincopy

<dependency>  

    <groupId>junit</groupId>  

    <artifactId>junit</artifactId>  

    <version>4.11</version>  

    <scope>test</scope>  

</dependency>  

 

然后在测试类中使用:

[java] view
plaincopy

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
2590a
);  

                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。


使用Mockito

怎么实现像JUnit那样的功能来测试方法insertRecord? 使用Mockito可以这样做:

首先在pom中添加Mockito依赖库:

[java] view
plaincopy

<dependency>  

    <groupId>org.mockito</groupId>  

    <artifactId>mockito-all</artifactId>  

    <version>1.9.5</version>  

    <scope>test</scope>  

</dependency>  

 

然后在测试类中增加如下代码:

[java] view
plaincopy

@RunWith(MockitoJUnitRunner.class)  

public class TestMyHBaseDAO{  

  @Mock   

  private HTableInterface table;  

  @Mock  

  private HTablePool hTablePool;  

  @Captor  

  private ArgumentCaptor putCaptor;  

  

  @Test  

  public void testInsertRecord() throws Exception {  

    //return mock table when getTable is called  

    when(hTablePool.getTable("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,然后用table接口和DAO来将它插入到表中。此过程将捕捉DAO用来inert操作的对象Put和检查rowkey, data1及 data2的值是否符合期望。

这里的重点是需要管理HTable pool和在DAO之外创建的HTable对象。这样会让你清晰的进行mock(创建虚拟对象)并像上面一样测试Put对象。近似的也可以这样测试Get, Scan, Delete等操作。


使用MRUnit

使用常规的数据访问来覆盖单元测试,让我们针对HBase表来实现MapReduce作业。

可以像测试MapReduce常规任务来测试HBase的MapReduce作业,MRUnit使得很容易完成这样的单元测试。

假设你有一个往HBase表写数据MR作业“MyTest”, 它的列族为“CF”。作业的reducer部分像以下这样:
[java] view plaincopypublic 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, lets 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中使用单元测试来测试reducer呢? 首先,在pom中添加MRUnit依赖库.

[java] view
plaincopy

<dependency>  

   <groupId>org.apache.mrunit</groupId>  

   <artifactId>mrunit</artifactId>  

   <version>1.0.0 </version>  

   <scope>test</scope>  

</dependency>  

 

 

接着,在测试类中像下面这样调用MRUnit提供的ReduceDriver:

[java] view
plaincopy

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 );  

   }  

  

}  

 

在MyReducer中进行的一系列过程,你会验证:

 
你所期望得到的输出结果。
对象Put以 “RowKey-1″为键插入到HBAse表中。
“DATADATA1DATA2″是列族CF和列CQ的值。

 

你也可以类似的使用MapperDriver来测试从HBase中获取数据的Mapper,或者测试从HBase读取、处理和写入数据到HDFS的MR作业。


使用HBase Mini-cluster

现在我们来看一下怎样实现集成测试。HBase附带了HBaseTestingUtility, 这个用于在简单的mini-cluster环境中编写集成测试。为了引用正确的库,在pom中需要添加如下依赖:

[java] view
plaincopy

<dependency>  

    <groupId>org.apache.hadoop</groupId>  

    <artifactId>hadoop-common</artifactId>  

    <version>2.0.0-cdh4.2.0</version>  

    <type>test-jar</type>  

    <scope>test</scope>  

</dependency>  

  

<dependency>  

    <groupId>org.apache.hbase</groupId>  

    <artifactId>hbase</artifactId>  

    <version>0.94.2-cdh4.2.0</version>  

    <type>test-jar</type>  

    <scope>test</scope>  

</dependency>  

          

<dependency>  

    <groupId>org.apache.hadoop</groupId>  

    <artifactId>hadoop-hdfs</artifactId>  

    <version>2.0.0-cdh4.2.0</version>  

    <type>test-jar</type>  

    <scope>test</scope>  

</dependency>  

  

<dependency>  

    <groupId>org.apache.hadoop</groupId>  

    <artifactId>hadoop-hdfs</artifactId>  

    <version>2.0.0-cdh4.2.0</version>  

    <scope>test</scope>  

</dependency>  

 

现在看一下怎样来为MyDAO的插入操作运行一个集成测试:

[java] view
plaincopy

public class MyHBaseIntegrationTest {  

private static HBaseTestingUtility utility;  

byte[] CF = "CF".getBytes();  

byte[] QUALIFIER = "CQ-1".getBytes();  

  

@Before  

public void setup() throws Exception {  

        utility = new HBaseTestingUtility();  

        utility.startMiniCluster();  

}  

  

@Test  

    public void testInsert() throws Exception {  

         HTableInterface table = utility.createTable(Bytes.toBytes("MyTest"),  

                         Bytes.toBytes("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"的HBase表,接着使用DAO插入一条记录、再从该表执行Get操作,验证DAO是否正确的插入记录。

同样可以测试更加复杂的MR作业。也可以在创建HBase集群,运行MR作业,输出数据到HBase表,验证插入的数据时访问HDFS及ZooKeeper mini-cluster。

注意:启动一个mini-cluster需要20到30秒,在Windows下需要安装Cygwin。然而它们应该只是周期性的运行,更长的时间也是可以接受的。 

可参考https://github.com/sitaula/HBaseTest,上面有同样的示例代码。Happy testing!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: