### Question:
Funnel { public <F> Batch<F, T> through( Function<? super List<F>, ? extends Collection<? extends T>> converter) { Batch<F, T> batch = new Batch<>(this, converter); batches.add(batch); return batch; }  Batch<F, T> through(
      Function<? super List<F>, ? extends Collection<? extends T>> converter); void add(T element); List<T> run();  }### Answer:
@Test public void testNulls() { new ClassSanityTester().testNulls(Funnel.class); new NullPointerTester().testAllPublicInstanceMethods(new Funnel<String>().through(batch::send)); }
@Test public void batchInvokedWithPostConversionThatThrows() { MyUncheckedException exception = new MyUncheckedException(); Funnel.Batch<Integer, String> toSpell = funnel.through(batch::send); Function<String, String> throwingFunction = s -> {throw exception;}; toSpell.accept(1, throwingFunction); toSpell.accept(2); when(batch.send(asList(1, 2))).thenReturn(asList("one", "two")); assertThrows(MyUncheckedException.class, funnel::run); Mockito.verify(batch).send(asList(1, 2)); Mockito.verifyNoMoreInteractions(batch); }
@Test public void batchInvokedWithAftereffectThatThrows() { MyUncheckedException exception = new MyUncheckedException(); Funnel.Batch<Integer, String> toSpell = funnel.through(batch::send); Consumer<String> throwingEffect = s -> {throw exception;}; toSpell.accept(1, throwingEffect); toSpell.accept(2); when(batch.send(asList(1, 2))).thenReturn(asList("one", "two")); assertThrows(MyUncheckedException.class, funnel::run); Mockito.verify(batch).send(asList(1, 2)); Mockito.verifyNoMoreInteractions(batch); }
@Test public void batchReturnsEmpty() { Funnel.Batch<Integer, String> toSpell = funnel.through(batch::send); toSpell.accept(1); when(batch.send(asList(1))).thenReturn(asList()); assertThrows(IllegalStateException.class, funnel::run); }
@Test public void batchReturnsLessThanInput() { Funnel.Batch<Integer, String> toSpell = funnel.through(batch::send); toSpell.accept(1); toSpell.accept(2); when(batch.send(asList(1, 2))).thenReturn(asList("one")); assertThrows(IllegalStateException.class, funnel::run); }
@Test public void batchReturnsMoreThanInput() { Funnel.Batch<Integer, String> toSpell = funnel.through(batch::send); toSpell.accept(1); when(batch.send(asList(1))).thenReturn(asList("one", "two")); assertThrows(IllegalStateException.class, funnel::run); }