如何单元测试Java Hbase API

How to unit test Java Hbase API

我正在使用 Java HBase API 从 Hbase 获取值。这是我的代码。

public class GetViewFromHbaseBolt extends BaseBasicBolt {

private HTable table;
private String zkQuorum;
private String zkClientPort;
private String tableName;

public GetViewFromHbaseBolt(String table, String zkQuorum,
        String zkClientPort) {
    this.tableName = table;
    this.zkQuorum = zkQuorum;
    this.zkClientPort = zkClientPort;
}

@Override
public void prepare(Map config, TopologyContext context) {
    try {
        table = getHTable();
    } catch (IOException e) {

        e.printStackTrace();
    }
}

@Override
public void execute(Tuple tuple, BasicOutputCollector collector) {
    try {
        if (tuple.size() > 0) {

            Long dtmid = tuple.getLong(0);

            byte[] rowKey = HBaseRowKeyDistributor.getDistributedKey(dtmid);
            Get get = new Get(rowKey);
            get.addFamily("a".getBytes());

            Result result = table.get(get);
            System.out.println(result);
            byte[] bidUser = result.getValue("a".getBytes(),
                    "co_created_5076".getBytes());
            collector.emit(new Values(dtmid, bidUser));
        }

    } catch (IOException e) {
        e.printStackTrace();

    }

}

@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {

    declarer.declare(new Fields("dtmi", "bidUser"));

}

// setting up the conections for Hbase
protected HTable getHTable() throws IOException {
    HTable table = null;
    Configuration conf;
    conf = HBaseConfiguration.create();
    conf.set("hbase.zookeeper.property.clientPort", zkClientPort);
    conf.set("hbase.zookeeper.quorum", zkQuorum);

    table = new HTable(conf, tableName);

    return table;
}

这很好用。现在我正在使用 Mockito API 编写单元测试。在我的测试中 class 我得到一个 java.langNullPointerException 当这个 when(table.get(any(Get.class))).thenReturn(result);被调用。

   public class GetViewFromHbaseBoltTest {

@Mock
private TopologyContext topologyContext;
@Mock
private  HTable table;
//HTable table = mock(HTable.class);


@Test
public void testExecute() throws IOException {

    long dtmId = 350000000770902930L;
    final byte[] COL_FAMILY = "a".getBytes();
    final byte[] COL_QUALIFIER = "co_created_5076".getBytes();

    // A mock tuple with a single dtmid
    Tuple tuple = mock(Tuple.class);
    when(tuple.size()).thenReturn(1);
    when(tuple.getLong(0)).thenReturn(dtmId);

    List<KeyValue> kvs = new ArrayList<KeyValue>();
    kvs.add(new KeyValue(COL_FAMILY, COL_QUALIFIER, Bytes
            .toBytes("ExpedtedBytes")));
    Result result = new Result(kvs);


    when(table.get(any(Get.class))).thenReturn(result);

    BasicOutputCollector collector = mock(BasicOutputCollector.class);

    GetViewFromHbaseBolt bolt = mock(GetViewFromHbaseBolt.class);
    // Execute the bolt.
    bolt.execute(tuple, collector);

    ArgumentCaptor<Values> valuesArg = ArgumentCaptor
            .forClass(Values.class);
    verify(collector).emit(valuesArg.capture());
    ArrayList<Object> d = valuesArg.getValue();

// verify

}

编辑

'Unit Test'是验证,验证一个特定的'unit of code'。如果 'unit of code' 依赖于外部调用,那么我们将这些调用模拟为 return 某些值。

如果您不从测试用例中调用 'actual unit of code',那么对代码进行单元测试有什么意义。明白了吗?

您应该只模拟来自实际方法的外部调用,而不是实际方法本身。

所以我会这样嘲笑:

@Mock
private HBaseRowKeyDistributor hDist;

@Mock
private HTable table;

@Test
public void sampleTest(){

    //Mock data and external calls

    final byte [] COL_FAMILY = "a".getBytes();
    final byte [] COL_QUALIFIER = "co_created_5076".getBytes();

    List<KeyValue> kvs =new ArrayList<KeyValue>();
    kvs.add(new KeyValue(COL_FAMILY, COL_QUALIFIER,
                           Bytes.toBytes("ExpedtedBytes")));
     Result result = new Result(kvs);

     byte[] rowKey = "Hello".getBytes();
     when(hdist.getDistributedKey(anyString()).thenReturn(rowKey);

     when(table.get(any(Get.class))).thenReturn(result);

     //Call the actual method
     calloriginalUnitTestingMethod();


     //Now Verify the result using Mockito verify like
     verify(...)

     //You could also intercept the attributes

}

也许您注意到您的代码测试不当table。可测试性差的原因是你的 class 做了两件事(创建一个 table 并执行一个元组)。我会建议您将 class 拆分为两个 classes 并将创建 table 的 class 注入执行元组的 class 中。

这 class 创建一个 Table:

public class HTableFactory {

    private String tablename;    

    public HTableFactory(String tablename) {
        this.tablename = tablename;
    }

    public HTable getTable() {}
}

然后你的 GetViewFromHbaseBolt class 看起来像下面这样:

public class GetViewFromHbaseBolt extends BaseBasicBolt {

    private HTableFactory factory;
    private HTable table;

    public GetViewFromHbaseBolt(HTableFactory factory) {}

    @Override
    public void prepare(Map config, TopologyContext context) {
        table = factory.getTable();
    }

    @Override
    public void execute(Tuple tuple, BasicOutputCollector collector) {}

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {}
}

现在您的 GetViewFromHbaseBolt 具有以下依赖项:

  • HTableFactory
  • Tuple
  • BasicOutputCollector
  • OutputFieldsDeclarer

在你的测试中class你可以模拟这个依赖并将它们注入GetViewFromHbaseBold

你检查过你的模拟初始化正确吗?我不是 Mockito 的专家,但在我看来,您需要将测试 运行ner 设置为 Mockito 运行ner 或显式初始化带注释的模拟 (@Mock)。例如,参见 Using @Mock and @InjectMocks

我的猜测是,当您 运行 生成 NullPointer 的行时,您的 "table" 变量为空。