问题描述
我将jmockit版本1.24与junit5一起使用,在这里我模拟了singleton类的公共方法,如下所示。
这是我的Test.java:
@Test
void mytest() {
MockUp mySingletonMock = new MockUp<MySingleton>() {
@Mock
public FileHeaders getHeader(String localFilePath) {
return new FileHeaders(checksum,"",new Date());
}
};
// Some assert statements
mySingletonMock.tearDown();
}
这是Singleton.java:
public class MySingleton {
private static MySingleton instance = new MySingleton();
private MySingleton(){
// Some initialization
}
public static MySingleton getInstance(){
return instance;
}
public FileHeaders getHeader(String localFilePath) {
...
}
}
我面对上述方法的问题,在myTest
完成执行之后执行的所有测试均失败,因为他们仍然看到模拟的getHeader
方法,而不是MySingleton类中的原始方法(我有使用调试语句验证了确实是这种情况。
如何防止在其他测试中看到这种getHeader
方法的模拟版本? (最好不更改jmockit的版本)。
所有这一切的怪异之处在于,使用maven在我的系统上本地运行的测试没有任何问题。但是当在teamcity上运行时会失败。
感谢您的帮助。谢谢。
编辑: 我尝试过的事情:
void resetMySingletonInstance() throws illegalaccessexception,InvocationTargetException,InstantiationException,NoSuchFieldException {
Constructor<?>[] constructors = MySingleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
// Verified that this gives a new instance
MySingleton instanceMySingleton = (MySingleton) theConstructor.newInstance();
Field ourInstanceField = MySingleton.class.getDeclaredField("ourInstance");
ourInstanceField.setAccessible(true);
ourInstanceField.set(null,instanceMySingleton);
}
解决方法
首先,我不会直接在测试方法中初始化和拆除模拟,而是使用setup()和tearDown()方法(@Before和@After注释)。
private List<MockUp<?>> mockUps = new ArrayList<>();
public void addMockUp(MockUp<?> mockUp) {
mockUps.add(mockUp);
}
@Before
public void setup() throws Exception {
addMockUp(new MockUp<MySingleton>() {
@Mock
public FileHeaders getHeader(String localFilePath) {
return new FileHeaders(checksum,"",new Date());
}
});
// other mocks ?
}
@Test
void myTest() {
// Some assert statements on MySingleton.getInstance()
}
@After
public void tearDown() {
mockUps.stream().forEach(mock->mock.tearDown());
}
但是测试中的问题是MySingleton是使用模拟对象初始化的。如果所有测试都在同一VM中执行,则该模拟对象将被重新用于后续测试。 我不确定为什么您在本地没有此问题,而仅在您的构建服务器上,也许是VM分叉的配置:https://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html
要解决此问题,您需要找到一种在每次测试后重新初始化单例的方法。 例如,一种解决方案是更改MySingleton,使其能够在测试后重置实例。如果无法同时访问MySingleton,则可以使用例如延迟加载并在tearDown方法中将其重置。
公共类MySingleton { 私有静态MySingleton实例;
private MySingleton(){
// Some initialization
}
public static MySingleton getInstance(){
if(instance == null) {
instance = new MySingleton();
}
return instance;
}
public static void reset() {
instance = null;
}
public FileHeaders getHeader(String localFilePath) {
...
}
}
如果您无法更改生产代码以通过反射重置字段,则还有另一种选择: Set Value of Private Static Field