private void assertEqualTables(int expectedRows, HBaseTestingUtil sourceCluster,
    TableName sourceTableName, HBaseTestingUtil targetCluster, TableName targetTableName,
    boolean ignoreTimestamps) throws Exception {
    try (Table sourceTable = sourceCluster.getConnection().getTable(sourceTableName);
      Table targetTable = targetCluster.getConnection().getTable(targetTableName);
      ResultScanner sourceScanner = sourceTable.getScanner(new Scan());
      ResultScanner targetScanner = targetTable.getScanner(new Scan())) {
      for (int i = 0; i < expectedRows; i++) {
        Result sourceRow = sourceScanner.next();
        Result targetRow = targetScanner.next();

        LOG.debug("SOURCE row: " + (sourceRow == null ? "null" : Bytes.toInt(sourceRow.getRow()))
          + " cells:" + sourceRow);
        LOG.debug("TARGET row: " + (targetRow == null ? "null" : Bytes.toInt(targetRow.getRow()))
          + " cells:" + targetRow);

        if (sourceRow == null) {
          fail("Expected " + expectedRows + " source rows but only found " + i);
        }
        if (targetRow == null) {
          fail("Expected " + expectedRows + " target rows but only found " + i);
        }
        Cell[] sourceCells = sourceRow.rawCells();
        Cell[] targetCells = targetRow.rawCells();
        if (sourceCells.length != targetCells.length) {
          LOG.debug("Source cells: " + Arrays.toString(sourceCells));
          LOG.debug("Target cells: " + Arrays.toString(targetCells));
          fail("Row " + Bytes.toInt(sourceRow.getRow()) + " has " + sourceCells.length
            + " cells in source table but " + targetCells.length + " cells in target table");
        }
        for (int j = 0; j < sourceCells.length; j++) {
          Cell sourceCell = sourceCells[j];
          Cell targetCell = targetCells[j];
          assertCellEquals(sourceCell, targetCell, () -> !ignoreTimestamps);
        }
      }
      Result sourceRow = sourceScanner.next();
      if (sourceRow != null) {
        fail("Source table has more than " + expectedRows + " rows.  Next row: "
          + Bytes.toInt(sourceRow.getRow()));
      }
      Result targetRow = targetScanner.next();
      if (targetRow != null) {
        fail("Target table has more than " + expectedRows + " rows.  Next row: "
          + Bytes.toInt(targetRow.getRow()));
      }
    }
  }
Basic test for the SyncTable M/R tool
hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSyncTable.java