Java 将Mockito验证模型与JUnit参数化测试相结合?

Java 将Mockito验证模型与JUnit参数化测试相结合?,java,unit-testing,junit,mockito,Java,Unit Testing,Junit,Mockito,起点 我想对一个类进行单元测试,该类基本上不创建输出本身,而是修改它接收的对象。确切地说:它委托给一个服务类,该服务类创建一个附加到对象的imageList: public class Class { //field declarations ... public Class(@Autowired Service service){ this.service = service; } public Object process(Object object){ //determ

起点
我想对一个类进行单元测试,该类基本上不创建输出本身,而是修改它接收的对象。确切地说:它委托给一个服务类,该服务类创建一个附加到对象的
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;
 }
}
到目前为止的工作
在我看来,测试这门课的最佳方法是:

  • 检查返回的产品是否与分配给
    过程的产品相同
  • 检查两个
    service
    方法被调用的频率(
    service
    当然是用Mockito模拟的;)
  • 现在我想结合JUnit创建参数化测试的能力。类似于:

    @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()},
     });
    }
    
    @参数
    公共静态列表参数(){
    返回Arrays.asList(新对象[][]{
    {optionA,Mockito.times(1),Mockito.never()},
    {optionB,Mockito.never(),Mockito.times(1)},
    {optionC,Mockito.never(),Mockito.never()},
    });
    }
    
    问题
    1.是否可以在参数化测试中传递静态函数?
    2.有没有特别的理由不这样做?
    3.有什么已知的替代方案吗


    提前感谢。

    从不
    返回实现。虽然您对调用语义保持警惕是正确的,但似乎有,所以您可以自由地将您的模式提取到一个变量并将其传递进来

    然而,请注意,VerificationMode实现可能是有状态的(我还没有机会深入挖掘),因此重用实例可能会导致奇怪的错误

    正如上面Florian Schaetz所提到的,您可以选择传递整数:
    never
    只是
    次(0)
    的别名,因此您可以将预期调用次数(0或1)作为JUnit参数传递,然后在测试中调用
    次(参数)
    ,而不必担心状态或副作用


    从Java 8开始,您可以在函数之间使用或传递代码作为数据,但结果可能不容易读取或维护,尤其是在
    对象[]中保留方法引用所需的强制转换时。

    @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());
      }
    }
    
    @RunWith(参数化的.class)
    公共类λ参数{
    公共静态整数toot(){return 0;}
    公共静态整数whistle(){return 1;}
    公共静态整数plunk(){return 2;}
    公共静态整数boom(){return 3;}
    私有静态供应商包装(供应商方法调用){
    返回方法调用;
    }
    @参数公共静态列表参数(){
    返回ImmutableList.of(
    //Java8知道静态调用“toot”实际上是一个供应商。。。
    新对象[]{(供应商)LambdaParameters::toot,0},
    //…但不能推断没有演员阵容。。。
    新对象[]{(供应商)lambdarameters::whistle,1},
    //…或者不通过上面的“wrap”方法获得提示。
    新对象[]{wrap(lambdarameters::plunk),2},
    //大多数lambda表达式用于调用可运行程序、侦听器、回调、,
    //和短函数或谓词,因此不需要强制转换。
    //您也可以将此语法用于紧凑的lambda函数。
    新对象[]{wrap(()->3),3},
    //正如您可能看到的,所有这些实际上都是匿名的内部类
    //在Java的早期版本中。
    新对象[]{new Supplier(){@Override public Integer get(){return LambdaParameters.boom();}},3}
    );
    }
    私人供应商;
    私有整数;
    公共LambdaParameters(供应商,整数){
    此项。供应商=供应商;
    this.integer=整数;
    }
    @测试公共无效供应商SuppliesExpectedInteger(){
    assertEquals(整数,supplier.get());
    }
    }
    

    这是否值得额外的逻辑完全取决于您有多少参数,有多少测试,以及是否有任何替代方案(如VerificationMode)。

    为什么需要传递静态函数?您在那里存储的是方法调用的结果,这可能会起作用。但是为什么呢?只需传递整数即可。0(=从不)或1(=一次),然后用
    Mockito.times(myInteger)调用它@FlorianSchaetz是的,没错。但我还是想知道是否可以将方法作为测试参数传递。也许我选择的例子不太中肯:)你能提供一些信息吗?这可能是最好的解决方案。我仍然想知道是否有可能将函数作为JUnit测试参数传递。您对Mockito(我猜是一般的单元测试)很有经验,所以您可能知道一些我可以考虑的源代码(不需要局限于JUnit)。您不能在Java中使用函数作为参数。时期当然,您可以使用一个作为参数调用函数的包装器。或者方法的名称作为参数,然后通过反射调用该参数。但是你不能简单地将函数本身作为参数,然后以某种方式调用它,你完全可以在Java8中使用函数作为参数,就像我在上面编辑的例子一样。从技术上讲,这个解决方案仍然使用包装器,但是因为Java生成了包装器,所以它可能是您正在寻找的答案,即使它可能不是解决您的问题的最佳解决方案。