我想测试一个静态方法(例如HobbyUtil.java:shareReadContext(int lineNumber)
),该方法将要求Singleton类(例如Shelf.java
)获取对象(例如Book
)以进一步获取值(例如上下文)返回的对象。
但是,在模拟案例执行期间,该Singleton类(即Shelf.java
)的ctor将触发链中的其他几个Singleton类(例如LibraryA.java
);并且其中之一会触发java.lang.ExceptionInInitializerError
。
我认为嘲笑/监视那些不相关的Singleton类超出了Mock Test的精神,因为它们与我的测试范围无关。
因此,我决定取消此Singleton类(即Shelf.java
)的私有ctor,只让该类中的getter方法(即getBook()
)返回具有我喜欢的行为的模拟对象,该对象将与我的行为完全匹配测试用例。
我遇到的问题是,我通过以下链接成功抑制了[[Shelf.java的ctor:suppress a singleton constructor in java with powermock
但是我无法弄清楚让此getter方法返回我想要的东西(即返回一个模拟对象)的方法。我正在使用的
PowerMockito,hamcrest和JUnit的版本在此处列出以供参考:
<dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>1.7.0</version><scope>test</scope></dependency>
<dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockit02</artifactId><version>1.7.0</version><scope>test</scope></dependency>
<dependency><groupId>org.hamcrest</groupId><artifactId>hamcrest-all</artifactId><version>1.3</version><scope>test</scope></dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
[我还用下面的示例类复制了案例,以显示遇到的问题(HobbyUtilTest.java是我的测试案例的类:]假设应用程序中有5个Java类:
public interface Book { public String getContext(int lineNumber); }
public class Shelf {
private static final Shelf shelf = new Shelf();
private String BOOK_NAME = "HarryPotter";
private Book book = null;
private Shelf() {
book = LibraryA.getInstance().getBook(BOOK_NAME);
// many relatively complicated logics are here in actual class ...
// I want to suppress this ctor
}
public static Shelf getInstance() {
return shelf;
}
public Book getBook() {
return book; // I want to return my mocked object while calling this method in test case
}
}
public class HobbyUtil {
public static String shareReadContext(int lineNumber){
String context = "";
Book book = Shelf.getInstance().getBook();
for (int i = 0 ; i < lineNumber; i++) {
context += book.getContext(i);
}
return context;
}
private HobbyUtil() {}
}
public class LibraryA {
private static final LibraryA library = new LibraryA();
private Book book;
private LibraryA() {
throw new java.lang.ExceptionInInitializerError();
}
public static LibraryA getInstance() {
return library;
}
public Book getBook(String bookName) {
return book;
}
}
public class BookImpl implements Book{
private String bookName = null;
BookImpl(String bookName){
this.bookName = bookName;
}
@Override
public String getContext(int lineNumber) {
return lineNumber + ": Context";
}
}
并且我会用shareReadContext(int lineNumber)
测试HobbyUtil.java
中的静态方法HobbyUtilTest.java
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.powermock.api.mockito.PowerMockito.when; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest(value = { Shelf.class }) public class HobbyUtilTest { @Test public void testShareReadContext() throws Exception { Book mockBook = PowerMockito.mock(Book.class); when(mockBook.getContext(anyInt())).thenReturn("context for test"); PowerMockito.suppress(PowerMockito.constructor(Shelf.class)); //PowerMockito.spy(Shelf.class); //when(Shelf.getInstance().getBook()).thenReturn(mockBook); // does not work //PowerMockito.doReturn(mockBook).when(Shelf.getInstance().getBook()); // does not work //PowerMockito.when(Shelf.class, "getBook").thenReturn(mockBook); // does not work //TODO any approach for it? String context = HobbyUtil.shareReadContext(1); assertThat(context, is("context for test")); } }
[任何人都可以帮助建议我如何让Book book = Shelf.getInstance().getBook();
中的HobbyUtil.java
行返回我的模拟对象(即mockBook
)?
Shelf
类位于程序包test
中,这应该有效: