【问题标题】:Mocking private field in servlet filter在 servlet 过滤器中模拟私有字段
【发布时间】:2012-12-06 14:15:24
【问题描述】:

是否可以在如下所示的类中模拟(当前使用 Mockito,也可能是其他测试库)字段“sf”:

public class SomeFilter implements Filter {

   private Logger log = Logger.getLogger(getClass());
   private SomeField sf = new SomeField();

   @Override
   public void init(FilterConfig fc) throws ServletException {
      log.info("");
   }

   @Override
   public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws     IOException, ServletException {
        fc.doFilter(request, response);
   }

   @Override
   public void destroy() {
      log.info("");
   }
}

如果有,怎么做?

【问题讨论】:

    标签: java servlets servlet-filters mockito


    【解决方案1】:

    是的。在您的测试类中,您可以使用 Spring Framework 中的 ReflectionTestUtils

    http://static.springsource.org/spring/docs/2.5.5/api/org/springframework/test/util/ReflectionTestUtils.html

    像这样调用setField 方法:

    SomeFilter filter = new SomeFilter();
    SomeField field = Mockito.mock(SomeField.class);
    ReflectionTestUtils.setField(filter , "sf", field);
    

    您可以像往常一样在测试用例中模拟对象。

    如果不能导入 Spring Framework,直接使用 Java 反射来做同样的事情并不太难。

    【讨论】:

    • 感谢您的想法和响应,但该项目不是基于 Spring 的,我不想为该项目添加仅用于测试的依赖。
    • 如果您使用 Maven,您可以将依赖项添加为测试范围,因此它仅在测试阶段使用。这样就不会在实际项目中增加任何开销。或者,您可能必须使用 Java 反射编写自己的
    【解决方案2】:

    我很想将字段设为protectedpackage-private,然后在您的测试中注入模拟,例如:

    final SomeField sf = mock(SomeField.class);
    someFilter.sf = sf;
    

    否则你可以提供一个构造函数来注入模拟:

    ...
    public SomeFilter() {
       this(new SomeField());
    }
    
    
    public SomeFilter(SomeField sf) {
        this.sf = sf;
    }
    ...
    

    然后在您的测试中,您可以像这样传递模拟:

    final SomeField sf = mock(SomeField.class);
    SomeFilter someFilter = new SomeFilter(sf);
    

    【讨论】:

    • 设计你的类以便它们与你的单元测试一起工作真的不是一个好主意!
    • @cowls 我不这么认为。我想我想要得到的是应该将依赖项提供给被测类。这将使测试更容易,并且应该有助于系统架构。
    • 同意。但是您可以使用反射将依赖项注入私有字段。因此,无需更改被测类即可使用您的测试
    • @cowls 这是真的,我并不反对你。这不是我会采取的方法。 :-)
    • 实际上,好的单元测试和好的类是齐头并进的,将代码设计成单元测试是一种很好的方法。目前,SomeFilter 和 SomeField 之间的耦合度非常高。如果这是一个可注入的接口会更好。
    【解决方案3】:

    考虑具有一些漂亮功能的PowerMock 框架,包括some integration with Mockito framework

    考虑this example 绕过封装并访问私有字段,如下所示

    String sf = Whitebox.getInternalState(o, "sf", String.class, B.class);
    Whitebox.setInternalState(o, "sf", "XXX", B.class);
    

    同时考虑(来自最后一个链接):

    所有这些事情都可以在不使用 PowerMock 的情况下实现,这只是普通的 Java 反射。然而反射需要很多样板代码并且容易出错,因此 PowerMock 为您提供了这些实用方法。 PowerMock 让您可以选择是否重构代码并添加 getter/setter 方法来检查/更改内部状态,或者是否使用其实用程序方法来完成相同的事情而不更改生产代码。这取决于你!

    【讨论】:

      【解决方案4】:

      您可以使用 PowerMock 模拟 SomeField 类的构造函数。来自documentation

      新对象的模拟构造

      1. 在 测试用例的类级别。使用 @PrepareForTest(ClassThatCreatesTheNewInstance.class) 注释在 测试用例的类级别。
      2. 使用PowerMock.createMock(NewInstanceClass.class) 创建一个模拟 应该构造的类的对象(我们称之为 模拟对象)。
      3. 使用 PowerMock.expectNew(NewInstanceClass.class).andReturn(mockObject) 到 期望类型对象的新构造 NewInstanceClass.class 而是返回模拟对象。
      4. 使用PowerMock.replay(mockObject, NewInstanceClass.class)更改 模拟对象和类重放模式,或者使用 PowerMock.replayAll() 方法。
      5. 使用PowerMock.verify(mockObject, NewInstanceClass.class)更改 模拟对象和类来验证模式,或者使用 PowerMock.verifyAll() 方法。

      如果你想通过Mockito syntax 使用powermock,那么它看起来像:

      whenNew(MyClass.class).withNoArguments().thenThrow(new IOException("error message"));
      

      【讨论】:

        猜你喜欢
        • 2012-06-28
        • 2010-10-09
        • 1970-01-01
        • 1970-01-01
        • 2013-07-14
        • 1970-01-01
        • 1970-01-01
        • 2023-03-25
        • 1970-01-01
        相关资源
        最近更新 更多