为了测试的目的,是否可以在Java中更改最终字段的值?

时间:2014-04-01 01:38:07

标签: java android mockito final junit3

我想我已经知道了这个问题的答案,但是问这个问题并没有什么坏处。

我有一个处理外部服务请求的类。该类有一个字段,可以帮助它保持服务率限制:
public static final int REQUEST_TIMEFRAME_MILLISECONDS = 600000;
由于该值是由服务强制执行的,并且不太可能发生变化,因此我将其标记为final,以便它不会被覆盖,并且对它们进行任何手动更改都会让人停下来思考。

一切正常,直到我开始测试。由于该值设置为10分钟,因此测试运行的时间比我想要的长得多。为了测试的目的,有没有什么方法可以正常使用该类,但值较低?或者我是否需要将其重构为可以通过子类更改的protected static int? 它是Android应用程序的一部分,我正在使用Junit3和Mockito进行测试,如果这有任何区别的话。

2 个答案:

答案 0 :(得分:2)

如果您从属性文件中读取值并限制此变量的范围,那么它也将满足您的要求。

答案 1 :(得分:1)

rpg711above链接到使用反射更改字段的方法,但我建议反对它。请记住,Java允许inline static final fields per JLS 13.4.9

  

如果字段是常量变量(§4.12.4),则删除关键字final或更改其值不会破坏与预先存在的二进制文件的兼容性,导致它们不能运行,但它们不会看到任何新值除非重新编译,否则使用该字段。即使用法本身不是编译时常量表达式(第15.28节)

,也是如此

JLS 17.5.3中也提到了警告,与linked answer中一样。

除此之外,还要记住,有人(也许是你的未来)有一天会阅读代码并做出(非常公平)假设static final int值永远不会改变。您可能只是在测试中更改它,但仍然在改变它,并且您可能会对下次您必须弄清楚测试失败的原因时的行为感到惊讶。


虽然可以放宽修饰符以使其成为非最终版protected static int,但我更喜欢“覆盖测试”或“测试过载”方法,具体取决于您是否像子类或可见测试方法。 Override-for-testing看起来像这样:

public class YourComponent {
  protected int getRequestTimeframeMilliseconds() {
    return 600000;
  }
}

public class YourComponentTest {
  private static class YourComponentForTesting extends YourComponent {
    @Override protected int getRequestTimeframeMilliseconds() {
      return 500;
    }
  }

  @Test public void shouldTimeout() { /* ... */ }
}

......这实际上非常有效,因为JVM可以inline the method。过载测试看起来像这样:

public class YourComponent {
  public static final int DEFAULT_REQUEST_TIMEFRAME_MILLISECONDS = 600000;

  public ReturnObject callService() {
    return callService(DEFAULT_REQUEST_TIMEFRAME_MILLISECONDS);
  }

  /** Package-private for testing. */
  ReturnObject callService(int timeframe) {
    /* Your implementation here. */
  }
}

如果您不介意覆盖,前者可能会很好,但如果您是依赖注入的粉丝,后者尤其方便。如果您将被测系统和测试放在同一个包中(即使它们位于不同的源树中),两者都可以从包私有可见性中受益。请注意,无论哪种方式,您都可以为API提供明显且简单的默认设置,但您明确表示有一种方法可以获得不同的行为。测试是组件的使用者,让他们通过API(而不是通过后门)修改行为可以帮助您定义组件的行为。