2

我想在 Mockito 中模拟一个类。然后它将发出一个 .newInstance() 调用,预计将返回一个实际的类实例(在我的情况下将返回一个模拟)。

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

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

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

下面是我要模拟的真实类,我无法更改它由接口定义的结构。我正在寻找的是一种在调用初始化时提供 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;
    }
}
4

2 回答 2

0

Mockito 专为模拟对象实例而设计。在底层,模拟方法实际上创建了一个代理,它接收对所有非最终方法的调用,并根据需要记录和存根这些调用。没有使用 Mockito 替换 Class 对象本身的函数的好方法。这给您留下了几个选择:

  1. 我没有使用 PowerMock 的经验,但它似乎是为模拟静态方法而设计的

  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(希望比我的简单实现更好,更线程安全),你可以使用纯 Mockito 来模拟工厂。如果这看起来像很多样板,那是因为它是,但如果你正在考虑切换到像Guice这样的 DI 解决方案,你可以减少很多。

  3. 考虑使一个字段或方法包私有或受保护,并记录它对测试目的可见。然后,您只能在测试代码中插入一个模拟实例。

    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
      }
    }
    
于 2012-10-25T00:25:21.030 回答
-1

我认为您只需要为以下内容引入一个额外的模拟Class

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

当然,诀窍是确保你的 mockedclazz.newInstance()不会被到处调用,因为由于类型擦除,你不能指定它实际上是一个Class<ArrayList>.

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

于 2012-10-24T23:50:15.497 回答