13

I know this is typically a bad practice but in my case it is necessary.

I have a case where an Enum holds a class to gain some information. So that Enum creates an instance of that calss in its Constructor.

public enum MyEnum {
    CONSTANT(new MyImpl());

    private final MyImpl myImpl;

    private MyEnum(final MyImpl impl) {
        this.myImpl = impl;
    }

    public void sayHello() {
        System.out.println(this.myImpl.getSomethingToSay());
    }

}

MyImpl.java is just a class with a single method that returns a String.

public class MyImpl {

    public String getSomethingToSay() {
        return "Hello!";
    }

}

Now finally the unit test:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;

@RunWith(MockitoJUnitRunner.class)
@PrepareForTest({ MyImpl.class, MyEnum.class })
public class MyEnumTest extends PowerMockTestCase {
    @Test
    public void testSmth2() throws Exception {
        MyImpl impl = Mockito.mock(MyImpl.class);
        Mockito.when(impl.getSomethingToSay()).thenReturn("It works!");
        PowerMockito.whenNew(MyImpl.class).withAnyArguments().thenReturn(impl);

        System.out.println(impl.getSomethingToSay());
        System.out.println(new MyImpl().getSomethingToSay());
        MyEnum.CONSTANT.sayHello();
    }
}

The output is:

It works!
Hello!
Hello!

But should be 3 times it works!

Zarathustra
  • 2,853
  • 4
  • 33
  • 62
  • 3
    Even if you can get `whenNew` to be used as many times as you want, it'll still only help if you can specify that *before* the enum is initialized - and if you can do that, it'll be used that way for *all* tests until you get a new classloader. I would really try to redesign this - mocking a dependency from an enum is really, really nasty. – Jon Skeet Dec 27 '13 at 08:37
  • But when i would move the mocking block in a static block, `System.out.println(new MyImpl().getSomethingToSay());` would still return `Hello!` – Zarathustra Dec 27 '13 at 08:46
  • I'm not quite sure how that's relevant... – Jon Skeet Dec 27 '13 at 08:53
  • I misunderstood your first comment. Okay then, what about mocking the Enum itself? – Zarathustra Dec 27 '13 at 08:58
  • No, I wouldn't do that either. I would change your design so that you don't *have* an enum which has a complex dependency (as a member variable) like this. You could consider taking the dependency as a parameter to methods within the enum, for example. – Jon Skeet Dec 27 '13 at 08:59
  • 1
    Does PowerMock let you mock Enums? Even if it does, I don't think it's a good idea. – Dawood ibn Kareem Dec 27 '13 at 11:41
  • @DawoodibnKareem of course its not a good idea :D – Zarathustra Jun 22 '17 at 17:03

1 Answers1

8

I found the faulty part.

I changed

@RunWith(MockitoJUnitRunner.class)

to

@RunWith(PowerMockRunner.class)

Now the mocking works. But i have to say that as Jon Skeet printed out, the enum does not have everywhere that mocked member-instance. So in another Unit test calling MyEnum.CONSTANT.sayHello(); will print again it works instead of Hello!.

Zarathustra
  • 2,853
  • 4
  • 33
  • 62