测试ENUM方法JUnit时出错

时间:2016-06-07 18:38:04

标签: java junit enums powermock

我正在尝试编写测试以确保我是否正在调用enum的方法但是收到错误:

@RunWith(PowerMockRunner.class)
@PrepareForTest({MonitorTask.class})
public class MonitorTest {

    @Test
    public void initialize()  {
        MonitorTask mockInstance = mock(MonitorTask.class);
        Whitebox.setInternalState(MonitorTask.class, "TIMER", mockInstance);

        Mockito.spy(mockInstance.initialize());
    }

}

这是我要测试的课程

public class Monitor {

    public  boolean initialize() {
        return MonitorTask.TIMER.initialize();
    }
}

ENUM:

public enum MonitorTask {
    TIMER;
    private final AtomicBoolean isPublishing = new AtomicBoolean(false);
    private final long          period       = ConfigUtils
            .requireLong("getMonitor");

    public synchronized boolean initialize() {
        return initialize(period, period);
    }


    boolean initialize(long delay, long period) {
        if (isPublishing.get()) {
            return false;
        }
        TimerTask task = new TimerTask() {
            @Override public void run() {
                try {
                    Publisher.INSTANCE.update(DateTime.now());
                } catch (Exception e) {
                    log.warn("Exception", e);
                }
            }
        };
        Timer timer = new Timer("MonitorTask", true); 
        timer.schedule(task, delay, period);
        isPublishing.set(true);
        return true;
    }
}

有人可以告诉我,我的测试有什么问题吗? 我得到的错误:

java.lang.ExceptionInInitializerError     at sun.reflect.GeneratedSerializationConstructorAccessor8.newInstance(Unknown Source)     at java.lang.reflect.Constructor.newInstance(Constructor.java:423)     at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:45)     at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)     在org.mockito.internal.creation.jmock.ClassImposterizer.createProxy(未知来源)     在org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(未知来源)     在org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(未知来源)     在org.mockito.internal.creation.CglibMockMaker.createMock(未知来源)     在org.powermock.api.mockito.internal.mockmaker.PowerMockMaker.createMock(PowerMockMaker.java:43)     在org.mockito.internal.util.MockUtil.createMock(未知来源)     在org.mockito.internal.MockitoCore.mock(未知来源)     在org.mockito.Mockito.mock(未知来源)     在org.mockito.Mockito.mock(未知来源)

1 个答案:

答案 0 :(得分:0)

首先:枚举常量表示为内部类,因此@PrepareForTest注释必须与内部类的类名一起使用 - 请参阅{{3进一步阅读。

除此之外:我喜欢使用枚举这样的"单身"对象也是。但使用PowerMock测试它们是错误的答案。 PowerMock依赖于字节代码操作(以避免这些枚举类是最终的,因此无法被静默覆盖)。而且严重的是:PowerMock和他的其他权力兄弟会带来更多麻烦而不是好事。我花了无数个小时来寻找奇怪的问题,从来没有在我的代码中找到错误。直截了当:使用PowerMock时,您可以忘记进行覆盖率测量。

我的解决方案"附带了更多的代码,但它允许测试而无需转向PowerMock ......

首先将所需的功能放入界面:

class TimerInitializationImpl implements TimerInitialization { ...

然后创建一个实现接口的普通类:

public enum MonitorTask implements TimerInitialization {
  TIMER;
  private final TimerInitialization impl = new TimerInitializationImpl();

  @Override
  public boolean initialize() { return impl.initialize() }
 ...

(请注意:可能你想保护那个impl类包保护......因为你只需要它一次:

@Test
public testThatEnumCallJustPasses() {
  assertThat(MonitorTask.TIMER.initialize(), is(false));

现在你可以:

a)为你的impl课程编写一个共同花园单元测试(不需要任何不健康的权力模拟)

b)如果你想:写另一个简单的"接线"测试...喜欢

TimerInitialization monitorTask = ... inserted via dependency injection OR from MonitorTask.TIMER
monitorTask.initialize()

或类似的东西,以证明你的枚举方法做了"某事"。

是的,这增加了一些样板;但严重的是:你总是想在单例枚举上设置一个界面。你看,直接使用枚举有一个令人讨厌的副作用:它强制你的组件之间相当硬的耦合(至少从测试的角度来看)!

因为迟早,您可能想要测试调用该枚举上的方法的代码;只有当你那时可以做一些像

这样的事情
MonitorTask.TIMER.whatever() ... 

如果您的所有客户端代码都是直接调用

{{1}}

然后,您的客户端代码无法正常测试。