Как насмехаться с классом одиночного Single Enum с использованием Mockito/Powermock?

Я не уверен, как издеваться над классом singleum singleton.

public enum SingletonObject{
  INSTANCE;
  private int num;

  protected setNum(int num) {
    this.num = num;
  }

  public int getNum() {
    return num;
  }

Я хотел бы заглушить getNum() в приведенном выше примере, но я не могу понять, как издеваться над фактическим классом SingletonObject. Я думал, что использование Powermock для подготовки теста поможет, поскольку перечисления по сути являются окончательными.

//... rest of test code
@Test
public void test() {
  PowerMockito.mock(SingletonObject.class);
  when(SingletonObject.INSTANCE.getNum()).thenReturn(1); //does not work
}

Это использует PowerMockMockito 1.4.10 и Mockito 1.8.5.

Ответ 1

Если вы хотите заглушить то, что возвращает INSTANCE, вы можете сделать это, но это отвратительно (используя рефлексию и манипуляцию байт-кодами). Я создал и протестировал простой проект с тремя классами, используя PowerMock 1.4.12/Mockito 1.9.0. Все классы были в одном пакете.

SingletonObject.java

public enum SingletonObject {
    INSTANCE;
    private int num;

    protected void setNum(int num) {
        this.num = num;
    }

    public int getNum() {
        return num;
    }
}

SingletonConsumer.java

public class SingletonConsumer {
    public String consumeSingletonObject() {
        return String.valueOf(SingletonObject.INSTANCE.getNum());
    }
}

SingletonConsumerTest.java

import static org.junit.Assert.*;
import static org.powermock.api.mockito.PowerMockito.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;

@RunWith(PowerMockRunner.class)
@PrepareForTest({SingletonObject.class})
public class SingletonConsumerTest {
    @Test
    public void testConsumeSingletonObject() throws Exception {
        SingletonObject mockInstance = mock(SingletonObject.class);
        Whitebox.setInternalState(SingletonObject.class, "INSTANCE", mockInstance);

        when(mockInstance.getNum()).thenReturn(42);

        assertEquals("42", new SingletonConsumer().consumeSingletonObject());
    }
}

Вызов Whitebox.setInternalState заменяет INSTANCE макетным объектом, с которым вы можете манипулировать в рамках своего теста.

Ответ 2

Иметь интерфейс с методами, которые вы намереваетесь издеваться

public interface SingletonInterface {
  int getNum();
}

Пусть enum реализует интерфейс

public enum SingletonObject implements SingletonInterface {
    INSTANCE;
    private int num;

    protected void setNum(int num) {
        this.num = num;
    }

    @Override
    public int getNum() {
        return num;
    }
}

Макет интерфейса

@Test
public void test() {
  SingletonInterface singleton = Mockito.mock(SingletonInterface.class);
  when(singleton.getNum()).thenReturn(1); //does work
}