private void assertTargetDoPutsFalse(int expectedRows, HBaseTestingUtil sourceCluster,
    TableName sourceTableName, HBaseTestingUtil targetCluster, TableName targetTableName)
    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())) {
      Result targetRow = targetScanner.next();
      Result sourceRow = sourceScanner.next();
      int rowsCount = 0;

      while (targetRow != null) {
        // only compares values for existing rows, skipping rows existing on
        // source only that were not added to target given --doPuts=false
        if (Bytes.toInt(sourceRow.getRow()) != Bytes.toInt(targetRow.getRow())) {
          sourceRow = sourceScanner.next();
          continue;
        }

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

        LOG.debug("rowsCount: " + rowsCount);

        Cell[] sourceCells = sourceRow.rawCells();
        Cell[] targetCells = targetRow.rawCells();
        int targetRowKey = Bytes.toInt(targetRow.getRow());
        if (targetRowKey >= 40 && targetRowKey < 60) {
          LOG.debug("Source cells: " + Arrays.toString(sourceCells));
          LOG.debug("Target cells: " + Arrays.toString(targetCells));
          fail("There shouldn't exist any rows between 40 and 60, since "
            + "Puts are disabled and Deletes are enabled.");
        } else if (targetRowKey >= 60 && targetRowKey < 70) {
          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()) + " shouldn't have same number of cells.");
          }
        } else if (targetRowKey >= 80 && targetRowKey < 90) {
          LOG.debug("Source cells: " + Arrays.toString(sourceCells));
          LOG.debug("Target cells: " + Arrays.toString(targetCells));
          fail("There should be no rows between 80 and 90 on target, as "
            + "these had different timestamps and should had been deleted.");
        } else if (targetRowKey >= 90 && targetRowKey < 100) {
          for (int j = 0; j < sourceCells.length; j++) {
            Cell sourceCell = sourceCells[j];
            Cell targetCell = targetCells[j];
            if (CellUtil.matchingValue(sourceCell, targetCell)) {
              fail("Cells values should not match for rows between " + "90 and 100. Target row id: "
                + Bytes.toInt(targetRow.getRow()));
            }
          }
        } else {
          for (int j = 0; j < sourceCells.length; j++) {
            Cell sourceCell = sourceCells[j];
            Cell targetCell = targetCells[j];
            assertCellEquals(sourceCell, targetCell, () -> true);
          }
        }
        rowsCount++;
        targetRow = targetScanner.next();
        sourceRow = sourceScanner.next();
      }
      assertEquals("Target expected rows does not match.", expectedRows, rowsCount);
    }
  }
Basic test for the SyncTable M/R tool
hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSyncTable.java