Mens Apache HBase-adoptionen til at bygge slutbrugerapplikationer er steget voldsomt, er mange af disse applikationer (og mange apps generelt) ikke blevet veltestet. I dette indlæg lærer du nogle af måderne, hvorpå denne test nemt kan udføres.
Vi vil starte med enhedstest via JUnit, derefter gå videre til at bruge Mockito og Apache MRUnit og derefter til at bruge en HBase mini-klynge til integrationstest. (HBase-kodebasen i sig selv er testet via en mini-klynge, så hvorfor ikke også udnytte det til upstream-applikationer?)
Som grundlag for diskussion, lad os antage, at du har et HBase-dataadgangsobjekt (DAO), der gør følgende indsættelse i HBase. Logikken kunne selvfølgelig være mere kompliceret, men for f.eks. skyld gør dette jobbet.
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())); returnere sætte; }}
HBaseTestObj er et grundlæggende dataobjekt med gettere og sættere for rowkey, data1 og data2.
InsertRecord laver en indsættelse i HBase-tabellen mod kolonnefamilien af CF, med CQ-1 og CQ-2 som kvalifikationer. CreatePut-metoden udfylder blot en Put og returnerer den til den kaldende metode.
Brug af JUnit
JUnit, som er velkendt af de fleste Java-udviklere på dette tidspunkt, kan nemt anvendes til mange HBase-applikationer. Tilføj først afhængigheden til din pom:
junit junit 4.11 test
Nu inden for testklassen:
public class TestMyHbaseDAOData { @Test public void testCreatePut() kaster Undtagelse { 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())); } }
Det, du gjorde her, var at sikre, at din createPut-metode opretter, udfylder og returnerer et Put-objekt med forventede værdier.
Brug af Mockito
Så hvordan går du om enhedsteste ovenstående insertRecord-metode? En meget effektiv tilgang er at gøre det med Mockito.
Først skal du tilføje Mockito som en afhængighed til din pom:
org.mockito mockito-all 1.9.5 test
Så i testklassen:
@RunWith(MockitoJUnitRunner.class)public class TestMyHBaseDAO{ @Mock privat HTableInterface-tabel; @Mock privat HTablePool hTablePool; @Captor privat ArgumentCaptor putCaptor; @Test public void testInsertRecord() kaster Undtagelse { //return mock table, når getTable kaldes when(hTablePool.getTable("tablename")).thenReturn(table); //opret testobjekt og foretag et kald til den DAO, der skal testes HBaseTestObj obj =new HBaseTestObj(); obj.setRowKey("ROWKEY-1"); obj.setData1("DATA-1"); obj.setData2("DATA-2"); MyHBaseDAO.insertRecord(tabel, 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"); }}
Her har du udfyldt HBaseTestObj med "ROWKEY-1", "DATA-1", "DATA-2" som værdier. Du brugte derefter den hånede tabel og DAO til at indsætte posten. Du fangede det Put, som DAO ville have indsat og verificeret, at rækketasten, data1 og data2 er, hvad du forventer, at de skal være.
Nøglen her er at administrere htable pool og htable instans oprettelse uden for DAO. Dette giver dig mulighed for at håne dem rent og teste Puts som vist ovenfor. På samme måde kan du nu udvide til alle de andre operationer såsom Hent, Scan, Slet og så videre.
Brug af MRUnit
Med almindelig testning af dataadgangsenheder dækket, lad os vende os mod MapReduce-job, der går imod HBase-tabeller.
At teste MR-job, der går imod HBase, er lige så ligetil som at teste almindelige MapReduce-job. MRUnit gør det virkelig nemt at teste MapReduce-opgaver inklusive HBase.
Forestil dig, at du har et MR-job, der skriver til en HBase-tabel, "MyTest", som har en kolonnefamilie, "CF". Reduktionen af et sådant job kunne se sådan ud:
public class MyReducer udvider TableReducer{ public static final byte[] CF ="CF".getBytes(); public static final byte[] QUALIFIER ="CQ-1".getBytes(); public void reduce(Text key, Iterable values, Context context) kaster IOException, InterruptedException {//en masse behandling for at udtrække data, der skal indsættes, i vores tilfælde, lad os sige, at vi simpelthen //tilføjer alle de poster, vi modtager fra mapperen for denne særlige //nøgle og indsæt én post i HBase StringBuffer data =new StringBuffer(); Put put =new Put(Bytes.toBytes(key.toString())); for (Tekst val:værdier) { data =data.append(val); } put.add(CF, QUALIFIER, Bytes.toBytes(data.toString())); //skriv til HBase context.write(new ImmutableBytesWritable(Bytes.toBytes(key.toString())), put); } }
Hvordan går du nu om at enhedsteste ovenstående reducer i MRUnit? Tilføj først MRUnit som en afhængighed til din pom.
org.apache.mrunit mrunit 1.0.0 test
Inden for testklassen skal du derefter bruge den ReduceDriver, som MRUnit leverer som nedenfor:
public class MyReducerTest { ReduceDriverreduceDriver; 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() kaster IOException { String strKey ="RowKey-1", strValue ="DATA", strValue1 ="DATA1", strValue2 ="DATA2"; List list =new ArrayList (); list.add(ny tekst(strVærdi)); list.add(ny tekst(strVærdi1)); list.add(ny tekst(strVærdi2)); //da i vores tilfælde er alt, hvad reducereren gør, at tilføje de poster, som kortlæggeren //sender den, bør vi få følgende tilbage String expectedOutput =strValue + strValue1 + strValue2; //Setup Input, efterlign hvilken mapper der ville have bestået //til reducer og kør test reduceDriver.withInput(new Text(strKey), list); //kør reduceringen og få dens output List > result =reduceDriver.run(); //udtræk nøgle fra resultatet og bekræft assertEquals(Bytes.toString(result.get(0).getFirst().get()), strKey); //udtræk værdi for CF/QUALIFIER og bekræft Sæt a =(Put)result.get(0).getSecond(); String c =Bytes.toString(a.get(CF, QUALIFIER).get(0).getValue()); assertEquals(expectedOutput,c ); }}
Dybest set, efter en masse behandling i MyReducer, bekræftede du, at:
- Udgangen er, hvad du forventer.
- Put, der er indsat i HBase, har "RowKey-1" som rækketasten.
- "DATADATA1DATA2" er værdien for CF-kolonnefamilien og CQ-kolonnekvalifikation.
Du kan også teste Mappers, der får data fra HBase på lignende måde ved hjælp af MapperDriver, eller teste MR-jobs, der læser fra HBase, behandler data og skriver til HDFS.
Brug af en HBase Mini-cluster
Nu vil vi se på, hvordan man går til integrationstest. HBase leveres med HBaseTestingUtility, som gør det nemt at skrive integrationstest med en HBase-miniklynge. For at trække de korrekte biblioteker ind, kræves følgende afhængigheder i din pom:
org.apache.hadoop hadoop-common 2.0.0-cdh4.2.0 test-jar type> test org.apache.hbase hbase 0.94.2-cdh4.2.0 test-jar test org.apache.hadoop hadoop-hdfs 2.0.0-cdh4.2.0 test-jar test org.apache.hadoop hadoop-hdfs 2.0.0-cdh4.2.0 test
Lad os nu se på, hvordan man kører gennem en integrationstest for MyDAO-indlægget beskrevet i introduktionen:
public class MyHBaseIntegrationTest {private static HBaseTestingUtility utility;byte[] CF ="CF".getBytes();byte[] QUALIFIER ="CQ-1".getBytes();@Beforepublic void setup() throws Exception { utility =new HBaseTestingUtility(); utility.startMiniCluster();}@Test public void testInsert() kaster Undtagelse { HTableInterface table =utility.createTable(Bytes.toBytes("MyTest"), Bytes.toBytes("CF")); HBaseTestObj obj =ny HBaseTestObj(); obj.setRowKey("ROWKEY-1"); obj.setData1("DATA-1"); obj.setData2("DATA-2"); MyHBaseDAO.insertRecord(tabel, obj); Get get1 =new Get(Bytes.toBytes(obj.getRowKey())); get1.addColumn(CF, CQ1); Resultat resultat1 =tabel.get(get1); assertEquals(Bytes.toString(result1.getRow()), obj.getRowKey()); assertEquals(Bytes.toString(result1.værdi()), obj.getData1()); Get get2 =new Get(Bytes.toBytes(obj.getRowKey())); get2.addColumn(CF, CQ2); Resultat resultat2 =tabel.get(get2); assertEquals(Bytes.toString(result2.getRow()), obj.getRowKey()); assertEquals(Bytes.toString(result2.value()), obj.getData2()); }}
Her oprettede du en HBase mini-klynge og startede den. Du oprettede derefter en tabel kaldet "MyTest" med en kolonnefamilie, "CF". Du indsatte en post ved hjælp af den DAO, du skulle teste, lavede en Get fra den samme tabel og bekræftede, at DAO'en indsatte poster korrekt.
Det samme kunne gøres for meget mere komplicerede brugssager sammen med MR-opgaverne som dem, der er vist ovenfor. Du kan også få adgang til HDFS- og ZooKeeper-miniklyngerne, der blev oprettet, mens du oprettede HBase-en, køre et MR-job, udlæse det til HBase og verificere de indsatte poster.
Bare en hurtig advarsel:opstart af en mini-klynge tager 20 til 30 sekunder og kan ikke udføres på Windows uden Cygwin. Men fordi de kun bør køres periodisk, bør den længere køretid være acceptabel.
Du kan finde eksempelkode til ovenstående eksempler på https://github.com/sitaula/HBaseTest. God test!