Unknown Id
Unknown Id

Reputation: 460

Combine Mockito VerificationModes with JUnit Parameterized Tests?

Starting Point
I wanted to unit-test a class that basically doesn't create an output itself, but modifies an object that it receives. To be precise: it delegates to a service class, that creates an image that is appended to the object's imageList:

public class Class {
 //field declarations ...

 public Class(@Autowired Service service){
  this.service = service;
 }

 public Object process(Object object){
  //determine property here ...

  if(property == optionA){
   //the service will add the new image A to a list in object
   this.service.createImageA(object);
  } else if(property == optionB){
   //the service will add the new image B to a list in object
   this.service.createImageB(object);
  }

  //object will be returned, with or without a new image
  return object;
 }
}

The Work So Far
The best way - in my opinion - to test this class is:

  1. to check if the product returned is the same as the one assigned to the process method
  2. check how often both servicemethods were invoked (service is mocked with Mockito, of course ;) )

Now I would like to combine this with JUnit's ability to create Parameterized tests. Something analogous to:

@Parameters
public static List<Object[]> parameters() {
 return Arrays.asList(new Object[][] {
            {optionA, Mockito.times(1), Mockito.never()},
            {optionB, Mockito.never(), Mockito.times(1)},
            {optionC, Mockito.never(), Mockito.never()},
 });
}

Question
1. Is it possible to pass static functions within a parameterized test?
2. Are there special reasons not to do this?
3. Any known alternatives?

Thanks in advance.

Upvotes: 1

Views: 2122

Answers (1)

Jeff Bowman
Jeff Bowman

Reputation: 95614

never and times return VerificationMode implementations. Though you're right to be wary of the calling semantics, there seem to be no side effects to VerificationMode creation, so you're free to extract your mode to a variable and pass it in.

Beware, however, that VerificationMode implementations might be stateful (I haven't had a chance to dig deeply), so reusing instances may cause bizarre errors.

As Florian Schaetz mentioned above, you may instead choose to pass integers: never is simply an alias for times(0), so you can pass the number of expected invocations (0 or 1) as a JUnit parameter and then call times(parameter) in your test without worrying about state or side effects.


As of Java 8, you can use method references or lambda expressions to pass code as data between fuunctions, but the result may not be easy to read or maintain, especially with the cast required to keep a method reference in an Object[]:

@RunWith(Parameterized.class)
public class LambdaParameters {

  public static Integer toot() { return 0; }
  public static Integer whistle() { return 1; }
  public static Integer plunk() { return 2; }
  public static Integer boom() { return 3; }

  private static Supplier<Integer> wrap(Supplier<Integer> methodCall) {
    return methodCall;
  }

  @Parameters public static List<Object[]> parameters() {
    return ImmutableList.of(
      // Java 8 knows that static call "toot" is effectively a Supplier<Integer>...
      new Object[] { (Supplier<Integer>) LambdaParameters::toot, 0 },
      // ...but it won't infer that without the cast...
      new Object[] { (Supplier<Integer>) LambdaParameters::whistle, 1 },
      // ...or without getting the hint through a method like "wrap" above.
      new Object[] { wrap(LambdaParameters::plunk), 2 },
      // Most lambda expressions are for calling Runnables, Listeners, Callbacks,
      // and short Functions or Predicates, so the casts there aren't necesssary.

      // You can use this syntax for compact lambda functions too.
      new Object[] { wrap(() -> 3), 3 },

      // All of these are effectively anonymous inner classes as you might see
      // in previous versions of Java.
      new Object[] { new Supplier<Integer>() { @Override public Integer get() { return LambdaParameters.boom(); }}, 3 }
    );
  }

  private Supplier<Integer> supplier;
  private Integer integer;

  public LambdaParameters(Supplier<Integer> supplier, Integer integer) {
    this.supplier = supplier;
    this.integer = integer;
  }

  @Test public void supplierSuppliesExpectedInteger() {
    assertEquals(integer, supplier.get());
  }
}

Whether this is worth the extra logic depends entirely on how many parameters you have, how many tests you have, and whether there are any alternatives (as there are for VerificationMode).

Upvotes: 2

Related Questions