def testCombiner(self, combiner, batches, expected_outputs):
    """Tests the provided combiner.

    Args:
      combiner: An object implementing the Combiner interface.
      batches: A list of batches, each is a tuples of ndarrays.  each ndarray
        represents the values of an input tensor of the analyzer over a single
        batch.
      expected_outputs: The expected outputs from extract_output.

    Exercises create_accumulator, add_input, merge_accumulators,
    and extract_output.
    """
    # Test serialization faithfully reproduces the object. If tests
    # mysteriously break, it could be because __reduce__ is missing something.
    combiner = pickle.loads(pickle.dumps(combiner))

    # Note `accumulators` is a generator, not list.  We do this to ensure that
    # add_input is not relying on its input being a list.
    accumulators = (
        combiner.add_input(combiner.create_accumulator(), batch)
        for batch in batches)

    final_accumulator = combiner.merge_accumulators(accumulators)
    outputs = combiner.extract_output(final_accumulator)
    tensor_infos = combiner.output_tensor_infos()
    self.assertEqual(len(outputs), len(expected_outputs))
    self.assertEqual(len(outputs), len(tensor_infos))
    for output, expected_output, tensor_info in zip(
        outputs, expected_outputs, tensor_infos):
      self.assertEqual(output.dtype, expected_output.dtype)
      self.assertEqual(tensor_info.dtype, tf.as_dtype(expected_output.dtype))

      self.assertAllClose(output, expected_output, rtol=1e-4, atol=1e-4)
Tests the provided combiner.

    Args:
      combiner: An object implementing the Combiner interface.
      batches: A list of batches, each is a tuples of ndarrays.  each ndarray
        represents the values of an input tensor of the analyzer over a single
        batch.
      expected_outputs: The expected outputs from extract_output.

    Exercises create_accumulator, add_input, merge_accumulators,
    and extract_output.
    
tensorflow_transform/analyzers_test.py