测试由于常量(最终)字段导致的顺序依赖性

时间:2015-06-01 23:30:37

标签: java unit-testing junit testng

修改单元测试常量的推荐模式是什么?这是我目前使用的一段时间,今天我意识到它存在缺陷。

假设我的班级看起来像这样

class Foo {
   private static final int MAX_TIME = Integer.getInteger("myProp", 1000);
   ...
}

我使用VM属性的事实不仅是在单元测试中修改它。这是因为我希望它从实际代码中不可变,我不想用它污染构造函数,因为它是一个实现细节。

所以,在制作中我希望超时1秒;但是对于单元测试来说太长了,所以我这样做了

class FooTest {
   @Before
   public void setUp() {
      System.setProperty("myProp", "50");
   }
   @Test
   public void testThatAssumesTimeoutOf50() { ... }
}

这很长时间以来一直很好,直到今天我终于添加了BarTest这样的

class BarTest {
   Foo mockFoo = mock(Foo.class); //actually done in a @Before method in the real code
}

现在创建的是我的测试失败或通过,具体取决于执行顺序。如果首先测试Foo,则假定测试超时为50ms的测试通过。但是,如果首先测试Bar,则会因为模拟而加载Foo,因此MAX_TIME采用默认值1000,然后BarTest结束时{{1}假设测试超时为50ms的测试现在失败了。

1 个答案:

答案 0 :(得分:0)

最简单的方法可能是删除final关键字。这将允许您在类加载时间后更改值。

由于您在许多不同的类和可能的包中设置并获取值,因此您应该添加getter和setter。如果您需要跨包更改此值,则必须为public

 class Foo{

   public static int getMaxTime(){
        return MAX_TIME;
   }

   public static  void changeMaxTime(int timeout){
        //validation logic goes here
        this.MAX_TIME = timeout;
   }

然后,在测试类中,您应该将旧值保存在@BeforeClass@Before阶段,将其更改为您想要的任何值,然后在{{1}中恢复旧值}或@AfterClass阶段。

因为看起来你在许多测试类中都这样做,所以最好自己制作一个Test Rule

@After

}

然后在你的测试代码中:

 public class TimeoutRule extends ExternalResource {

   private final int oldTimeout, newTimeout;

   public TimeoutRule(int timeout){
         this.oldTimeout = Foo.getMaxTime();
         this.newTimeout = timeout;
   }

    @Override
    protected void before() {

    }

   @Override
   protected void after() {
       //restore old timeout
        Foo.setMaxTimeOut(oldTimeout);
}

}

该代码会将超时重置为50 ms,然后在每次测试后将其恢复为原始值。如果您只希望每个测试类只执行一次,请改用class FooTest { @Rule public TimeoutRule timeoutRule = new TimeoutRule(50); @Test public void testThatAssumesTimeoutOf50() { ... } (并创建字段@ClassRule