我如何模拟Class <! - ?在Mockito中扩展List - > myVar?

时间:2012-10-24 21:20:36

标签: class mocking mockito powermock

我想在Mockito中嘲笑一个班级。然后它将发出一个.newInstance()调用,该调用将返回一个实际的类实例(并且在我的情况下将返回一个mock)。

如果设置正确,那么我可以这样做:

ArrayList myListMock = mock(ArrayList.class);
when(myVar.newInstance()).thenReturn(myListMock);

我知道我可以设置它以便类ArrayList的新实例将是一个模拟(使用PowerMockito whenNew),只是想知道是否有办法模拟这种类对象所以我不必覆盖实例创建...

下面是我试图模拟的真正的类,我不能改变它由接口定义的结构。我正在寻找的是一种在调用initialize时提供cvs的方法。

public class InputConstraintValidator 
    implements ConstraintValidator<InputValidation, StringWrapper> {

    Class<? extends SafeString> cvs;

    public void initialize(InputValidation constraintAnnotation) {
        cvs = constraintAnnotation.inputValidator();
    }

    public boolean isValid(StringWrapper value, 
                   ConstraintValidatorContext context) {

        SafeString instance;
        try {
             instance = cvs.newInstance();
        } catch (InstantiationException e) {
            return false;
        } catch (IllegalAccessException e) {
            return false;
    }
}

2 个答案:

答案 0 :(得分:0)

Mockito专门用于模拟对象的实例。在引擎盖下,mock方法实际上创建了一个代理,它接收对所有非final方法的调用,并根据需要记录和存根这些调用。 使用Mockito替换Class对象本身的函数是没有好办法的。这给你留下了一些选择:

  1. 我没有PowerMock的经验,但似乎是designed for mocking static methods

  2. 在依赖注入样式中,将静态工厂方法设置为工厂实例。由于看起来您实际上并未使用ArrayList,因此请说明您的课程为FooBar

    class FooBar {
      static class Factory {
        static FooBar instance;
        FooBar getInstance() {
          if (instance == null) {
            instance = new FooBar();
          }
          return instance;
        }
      }
      // ...
    }
    

    现在,您的类用户可以接收new FooBar.Factory()参数,该参数以单例样式创建您的真实FooBar(希望better and more threadsafe比我的简单实现),并且您可以使用纯Mockito来模拟Factory。如果这看起来像是很多样板,那是因为它是,但是如果你想切换到像Guice这样的DI解决方案,你可以减少很多。

  3. 考虑将字段或方法包设为私有或受保护,并将其记录为visible for testing purposes。然后,您只能在测试代码中插入模拟实例。

    public class InputConstraintValidator implements 
        ConstraintValidator<InputValidation, StringWrapper> {
      Class<? extends SafeString> cvs;
    
      public void initialize(InputValidation constraintAnnotation) {
        cvs = constraintAnnotation.inputValidator();
      }
    
      public boolean isValid(StringWrapper value,
          ConstraintValidatorContext context) {
        SafeString instance;
        try {
          instance = getCvsInstance();
        } catch (InstantiationException e) {
          return false;
        } catch (IllegalAccessException e) {
          return false;
        }
      }
    
      @VisibleForTesting protected getCvsInstance()
          throws InstantiationException, IllegalAccessException {
        return cvs.newInstance();
      }
    }
    
    public class InputConstaintValidatorTest {
      @Test public void testWithMockCvs() {
        final SafeString cvs = mock(SafeString.class);
        InputConstraintValidator validator = new InputConstraintValidator() {
          @Override protected getCvsInstance() {
            return cvs;
          }
        }
        // test
      }
    }
    

答案 1 :(得分:-1)

我认为你只需要为Class引入一个额外的模拟:

ArrayList<?> myListMock = mock(ArrayList.class);
Class<ArrayList> clazz = mock(Class.class);
when(clazz.newInstance()).thenReturn(myListMock);

当然诀窍在于确保你的模拟clazz.newInstance()最终不会被调用,因为由于类型擦除,你无法指定它实际上是Class<ArrayList>。< / p>

另外,要小心定义自己的模拟作为基本的ArrayList - 通常我会使用“真实的”并用模拟填充它。