0

I am trying to write test to ensure whether I am calling the enum's method or not but getting error :

@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());
    }

}

This is the class I am trying to test

public class Monitor {

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

The 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;
    }
}

Can some one tell me is there anything wrong with my test ? The Error I am getting :

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) at org.mockito.internal.creation.jmock.ClassImposterizer.createProxy(Unknown Source) at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(Unknown Source) at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(Unknown Source) at org.mockito.internal.creation.CglibMockMaker.createMock(Unknown Source) at org.powermock.api.mockito.internal.mockmaker.PowerMockMaker.createMock(PowerMockMaker.java:43) at org.mockito.internal.util.MockUtil.createMock(Unknown Source) at org.mockito.internal.MockitoCore.mock(Unknown Source) at org.mockito.Mockito.mock(Unknown Source) at org.mockito.Mockito.mock(Unknown Source)

user3559583
  • 33
  • 1
  • 7

1 Answers1

0

First of all: enum constants are represented as inner classes, therefore the @PrepareForTest annotation must be used with the class names of those inner classes - see here for further reading.

Beyond that: I like using enums for such "singleton" objects, too. But using PowerMock to test them is the wrong answer. PowerMock relies on byte code manipulation (to circumvent the fact that those enum classes are final and thus can't be silently overridden). And seriously: PowerMock and his other power cousins cause more trouble than good. I have spent countless hours hunting down bizarre problems to never find a bug in my code. And straight forward: when using PowerMock, you can forget about doing coverage measurements for example.

My "solution" comes with a bit more code, but it allows for testing without turning to PowerMock...

You start with putting the function(s) you want into an interface:

public interface TimerInitialization { boolean initialize() ...

Then you create a normal class that implements the interface:

class TimerInitializationImpl implements TimerInitialization { ...

(please note: probably you want to keep that impl class package protected ... as you only need it once:

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

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

Now you can:

a) write a common-of-garden unit test for your impl class (that doesn't require any unhealthy power mocking)

b) if you want to: write another simple "wiring" test ... like

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

or something alike to prove that your enum methods do "something".

Yes, this adds some boilerplate; but seriously: you always want to put an interface on singleton enums anyway. You see, using enums directly has a nasty side effect: it enforces pretty hard coupling between your components (at least from a testing perspective)!

Because sooner or later, you might want to test code that calls methods on that enum; and only when you then can do something like

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

If all your client code is directly calling

MonitorTask.TIMER.whatever() ... 

then again, your client code can't be tested normally.

GhostCat
  • 137,827
  • 25
  • 176
  • 248