如何使用 Mockito/Powermock 模拟枚举单例类?
Posted
技术标签:
【中文标题】如何使用 Mockito/Powermock 模拟枚举单例类?【英文标题】:How to mock an enum singleton class using Mockito/Powermock? 【发布时间】:2013-04-03 01:43:45 【问题描述】:我不确定如何模拟枚举单例类。
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。
【问题讨论】:
检查类似的线程:***.com/questions/2302179/mocking-a-singleton-class 感谢 Martin,我通读了该线程,它看起来像是一种使用非枚举方式实现单例的方法,并且我能够使用该方法正确使用模拟。但是,有没有办法模拟一个枚举单例类?从我收集到的信息来看,枚举单例类是在 java 1.5 之后声明单例的推荐方法。 Re: PowerMock : can i mock enums? 单身人士是邪恶的 :) 【参考方案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
替换为您可以在测试中操作的模拟对象。
【讨论】:
但是你怎么能模拟 SingletonObject.class 呢?它会给 MockitoException 注释“Mockito 无法模拟/监视以下内容:-最终类-匿名类-原始类型” 这使用了 PowerMock。 嗨,我在使用Whitebox.setInternalState
时收到了java.lang.IllegalAccessException: Can not set static final
。你知道为什么吗?
你在你的测试类中使用了@PrepareForTest
注解吗?
我尝试了同样的方法,但我的枚举的构造函数仍在执行,这是我不想要的。上述解决方案是否也模拟了枚举的构造函数?【参考方案2】:
与您打算模拟的方法有一个接口
public interface SingletonInterface
int getNum();
让枚举实现接口
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
【讨论】:
这个答案是最好的【参考方案3】:除了上面的 Matt Lachman 回答之外,在SingleTonConsumerTest.class
中创建一个用于 power mock 的对象工厂
@ObjectFactory
public IObjectFactory getObjectFactory()
return new org.powermock.modules.testng.PowerMockObjectFactory();
这将删除Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types
错误。
【讨论】:
【参考方案4】:我不确定这是否是最好的方法,但就我而言,到目前为止它运行良好。我正在使用 mockito-core 和 mockito-inline (3.11.0),我在间谍的帮助下嘲笑我的单身人士。您的班级示例:
SingletonObject so = SingletonObject.INSTANCE;
private SingletonObject spySo = Mockito.spy(so);
@Before
public void setUp()
Mockito.doReturn(10).when(spySo).getNum();
@Test
public void simpleTest()
assertThat(spySo.getNum(), is(10));
【讨论】:
【参考方案5】:Mockito 文档在 enum 用例上没有明确说明:
Mocking final types, enums and final methods (Since 2.1.0)Powermock 支持:
兼容 JUnit 4build.gradle(** 也应该与 Maven 一起使用)
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
testImplementation 'org.mockito:mockito-inline:4.0.0'
testImplementation 'org.mockito:mockito-junit-jupiter:4.1.0'
关于可用的实验功能(Powermock + JUnit 5),可以尝试一下:
implementation group: 'org.powermock', name: 'powermock-module-junit5', version: '1.6.4'
testImplementation 'org.powermock:powermock-mockito-release-full:1.6.4'
枚举:
public enum ConnectionFactory
INSTANCE;
public Connection get() throws SQLException
//
// Load url from configuration file ('application.yml')
//
final var connection = DriverManager.getConnection(url);
connection.setAutoCommit(false);
return connection;
可以使用这些方法:
class DatabaseUnitTests
private final ConnectionFactory connectionFactory = Mockito.spy(ConnectionFactory.INSTANCE);
//Or
@InjectMocks
private final ConnectionFactory connectionFactory = Mockito.mock(ConnectionFactory.class);
@Test
void check_is_database_connection_is_OK()
Mockito.doReturn(mockedConnection()).when(connectionFactory).get();
// Do something
【讨论】:
以上是关于如何使用 Mockito/Powermock 模拟枚举单例类?的主要内容,如果未能解决你的问题,请参考以下文章
模拟系统类时的 Mockito + PowerMock LinkageError
单元测试及框架简介 --junitjmockmockitopowermock的简单使用