PowerMockito:如果代码在另一个 class 中,则无法模拟构造函数
PowerMockito: Unable to mock a constructor if code is in another class
我遇到了一个很奇怪的问题。
以下是我的测试class:
SearchViewModel.java
@RunWith(PowerMockRunner.class)
@PrepareForTest({JSONReader.class, ConstantsPath.class, DatabaseManager.class})
public class SearchViewModelTest {
@Rule
public TestRule rule = new InstantTaskExecutorRule();
private String indexSearchContent;
private String fullTextSearchContentGL1, fullTextSearchContentGL2, fullTextSearchContentGL3, fullTextSearchContentGL4;
private String searchQuery = "a";
private List<FullTextSearchItem> fullTextSearchResult;
private String behaviorString;
private SearchViewModel searchViewModel;
private DatabaseManager databaseManager;
private void initInputs() throws IOException {
indexSearchContent = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getIndexSearchFilePath()));
behaviorString = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getBehavioralFilePath()));
fullTextSearchContentGL1 = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getFullTextSearchFilePath("1")));
fullTextSearchContentGL2 = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getFullTextSearchFilePath("2")));
fullTextSearchContentGL3 = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getFullTextSearchFilePath("3")));
}
private void mockDaggerDependency() {
AppInfo appInfo = Mockito.mock(AppInfo.class);
Mockito.when(appInfo.getAppName()).thenReturn("testApp");
Mockito.when(appInfo.getAppLanguage()).thenReturn("EN");
TestApplicationModule module = new TestApplicationModule(appInfo);
DatabaseModule databaseModule = Mockito.mock(DatabaseModule.class);
Component component = DaggerComponent.builder().applicationModule(module).databaseModule(databaseModule).build();
MyApplication.setComponent(component);
}
private void mockGuidelineList() throws Exception {
databaseManager = PowerMockito.mock(DatabaseManager.class);
List<Guideline> mockedGls = new ArrayList<>();
Guideline gl = new Guideline();
gl.setGuidelineId("1");
mockedGls.add(gl);
gl = new Guideline();
gl.setGuidelineId("2");
mockedGls.add(gl);
gl = new Guideline();
gl.setGuidelineId("3");
mockedGls.add(gl);
Mockito.when(databaseManager.getGuidelinesListByPositionOnHome()).thenReturn(mockedGls);
PowerMockito.whenNew(DatabaseManager.class).withNoArguments().thenReturn(databaseManager);
// prepare expected output for fulltext search
Observable.fromIterable(new DatabaseManager().getGuidelinesListByPositionOnHome())
.map(Guideline::getGuidelineId)
.flatMap(glId -> BehavioralFile.<List<FullTextSearchItem>>loadJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath(glId),
new TypeToken<List<FullTextSearchItem>>() {
}.getType()).toObservable()
.flatMapIterable(fullTextSearchitems -> fullTextSearchitems)
.filter(item -> item.getText().toLowerCase().contains(searchQuery.toLowerCase()))).<List<FullTextSearchItem>>toList()
.subscribe(list -> {
fullTextSearchResult = list;
});
}
@Before
public void setUp() throws Exception {
MainActivityTest.overrideRxJavaPlugins();
mockDaggerDependency();
initInputs();
PowerMockito.mockStatic(JSONReader.class);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getIndexSearchFilePath())).willReturn(indexSearchContent);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath("1"))).willReturn(fullTextSearchContentGL1);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath("2"))).willReturn(fullTextSearchContentGL2);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath("3"))).willReturn(fullTextSearchContentGL3);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getBehavioralFilePath())).willReturn(behaviorString);
mockGuidelineList();
searchViewModel = new SearchViewModel();
}
@After
public void tearDown() throws Exception {
}
@Test
public void loadFullTextSearch() throws Exception {
//searchViewModel.loadFullTextSearch_(searchQuery);
loadFullTextSearch(searchQuery);
assertEquals(searchViewModel.fullTextSearchListLiveData.getValue().size(), fullTextSearchResult.size());
}
private void loadFullTextSearch(String query) {
// following line is throwing exception if put in another class.
Observable.fromIterable(new DatabaseManager().getGuidelinesListByPositionOnHome())
.map(Guideline::getGuidelineId)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.flatMap(glId -> BehavioralFile.<List<FullTextSearchItem>>loadJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath(glId),
new TypeToken<List<FullTextSearchItem>>() {
}.getType()).toObservable()
.flatMapIterable(fullTextSearchitems -> fullTextSearchitems)
.filter(item -> item.getText().toLowerCase().contains(query.toLowerCase()))).<List<FullTextSearchItem>>toList().toObservable()
.subscribe(list -> searchViewModel.fullTextSearchListLiveData.setValue(list));
}
}
这里 loadFullTextSearch()
测试用例工作得很好,直到我删除行 searchViewModel.loadFullTextSearch_(searchQuery);
,
的注释
检查修改后的测试用例:
@Test
public void loadFullTextSearch() throws Exception {
searchViewModel.loadFullTextSearch_(searchQuery);
//loadFullTextSearch(searchQuery);
assertEquals(searchViewModel.fullTextSearchListLiveData.getValue().size(), fullTextSearchResult.size());
}
奇怪的部分来了: 两个函数(earchViewModel.loadFullTextSearch_()
、loadFullTextSearch()
)具有相同的代码,但 loadFullTestSearch_()
在 SearchViewModel class 和 loadFullTextSearch()
是测试用例本身,我这样做是为了弄清楚为什么 DatabaseManager class 的构造函数在代码位于 SearchViewModel
class.
注意: 我正在模拟 DatabaseManager class 的构造函数。检查 mockGuidelineList()
方法。如果在同一个测试中调用构造函数,则模拟方法有效 class。
我得到的异常是:
java.lang.NullPointerException: Cannot return null from a non-@Nullable @Provides method
因为我正在使用 dagger 并且我正在模拟的构造函数初始化数据库对象依赖项。
public DatabaseManager() {
MyApplication.getComponent().inject(this);
}
任何帮助将不胜感激,谢谢。
我做的一切都很好但是错过了添加 class 在 @PrepareForTest
注释中调用构造函数的地方。
所以在我的情况下,这必须是:
@PrepareForTest({JSONReader.class, ConstantsPath.class, DatabaseManager.class, SearchViewModel.class})
this Whosebug 的回答帮助了我。
我遇到了一个很奇怪的问题。
以下是我的测试class:
SearchViewModel.java
@RunWith(PowerMockRunner.class)
@PrepareForTest({JSONReader.class, ConstantsPath.class, DatabaseManager.class})
public class SearchViewModelTest {
@Rule
public TestRule rule = new InstantTaskExecutorRule();
private String indexSearchContent;
private String fullTextSearchContentGL1, fullTextSearchContentGL2, fullTextSearchContentGL3, fullTextSearchContentGL4;
private String searchQuery = "a";
private List<FullTextSearchItem> fullTextSearchResult;
private String behaviorString;
private SearchViewModel searchViewModel;
private DatabaseManager databaseManager;
private void initInputs() throws IOException {
indexSearchContent = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getIndexSearchFilePath()));
behaviorString = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getBehavioralFilePath()));
fullTextSearchContentGL1 = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getFullTextSearchFilePath("1")));
fullTextSearchContentGL2 = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getFullTextSearchFilePath("2")));
fullTextSearchContentGL3 = JSONReader.convertStreamToString(getClass().getClassLoader().getResourceAsStream(ConstantsPath.getInstance().getFullTextSearchFilePath("3")));
}
private void mockDaggerDependency() {
AppInfo appInfo = Mockito.mock(AppInfo.class);
Mockito.when(appInfo.getAppName()).thenReturn("testApp");
Mockito.when(appInfo.getAppLanguage()).thenReturn("EN");
TestApplicationModule module = new TestApplicationModule(appInfo);
DatabaseModule databaseModule = Mockito.mock(DatabaseModule.class);
Component component = DaggerComponent.builder().applicationModule(module).databaseModule(databaseModule).build();
MyApplication.setComponent(component);
}
private void mockGuidelineList() throws Exception {
databaseManager = PowerMockito.mock(DatabaseManager.class);
List<Guideline> mockedGls = new ArrayList<>();
Guideline gl = new Guideline();
gl.setGuidelineId("1");
mockedGls.add(gl);
gl = new Guideline();
gl.setGuidelineId("2");
mockedGls.add(gl);
gl = new Guideline();
gl.setGuidelineId("3");
mockedGls.add(gl);
Mockito.when(databaseManager.getGuidelinesListByPositionOnHome()).thenReturn(mockedGls);
PowerMockito.whenNew(DatabaseManager.class).withNoArguments().thenReturn(databaseManager);
// prepare expected output for fulltext search
Observable.fromIterable(new DatabaseManager().getGuidelinesListByPositionOnHome())
.map(Guideline::getGuidelineId)
.flatMap(glId -> BehavioralFile.<List<FullTextSearchItem>>loadJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath(glId),
new TypeToken<List<FullTextSearchItem>>() {
}.getType()).toObservable()
.flatMapIterable(fullTextSearchitems -> fullTextSearchitems)
.filter(item -> item.getText().toLowerCase().contains(searchQuery.toLowerCase()))).<List<FullTextSearchItem>>toList()
.subscribe(list -> {
fullTextSearchResult = list;
});
}
@Before
public void setUp() throws Exception {
MainActivityTest.overrideRxJavaPlugins();
mockDaggerDependency();
initInputs();
PowerMockito.mockStatic(JSONReader.class);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getIndexSearchFilePath())).willReturn(indexSearchContent);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath("1"))).willReturn(fullTextSearchContentGL1);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath("2"))).willReturn(fullTextSearchContentGL2);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath("3"))).willReturn(fullTextSearchContentGL3);
BDDMockito.given(JSONReader.readJsonFile(ConstantsPath.getInstance().getBehavioralFilePath())).willReturn(behaviorString);
mockGuidelineList();
searchViewModel = new SearchViewModel();
}
@After
public void tearDown() throws Exception {
}
@Test
public void loadFullTextSearch() throws Exception {
//searchViewModel.loadFullTextSearch_(searchQuery);
loadFullTextSearch(searchQuery);
assertEquals(searchViewModel.fullTextSearchListLiveData.getValue().size(), fullTextSearchResult.size());
}
private void loadFullTextSearch(String query) {
// following line is throwing exception if put in another class.
Observable.fromIterable(new DatabaseManager().getGuidelinesListByPositionOnHome())
.map(Guideline::getGuidelineId)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.flatMap(glId -> BehavioralFile.<List<FullTextSearchItem>>loadJsonFile(ConstantsPath.getInstance().getFullTextSearchFilePath(glId),
new TypeToken<List<FullTextSearchItem>>() {
}.getType()).toObservable()
.flatMapIterable(fullTextSearchitems -> fullTextSearchitems)
.filter(item -> item.getText().toLowerCase().contains(query.toLowerCase()))).<List<FullTextSearchItem>>toList().toObservable()
.subscribe(list -> searchViewModel.fullTextSearchListLiveData.setValue(list));
}
}
这里 loadFullTextSearch()
测试用例工作得很好,直到我删除行 searchViewModel.loadFullTextSearch_(searchQuery);
,
检查修改后的测试用例:
@Test
public void loadFullTextSearch() throws Exception {
searchViewModel.loadFullTextSearch_(searchQuery);
//loadFullTextSearch(searchQuery);
assertEquals(searchViewModel.fullTextSearchListLiveData.getValue().size(), fullTextSearchResult.size());
}
奇怪的部分来了: 两个函数(earchViewModel.loadFullTextSearch_()
、loadFullTextSearch()
)具有相同的代码,但 loadFullTestSearch_()
在 SearchViewModel class 和 loadFullTextSearch()
是测试用例本身,我这样做是为了弄清楚为什么 DatabaseManager class 的构造函数在代码位于 SearchViewModel
class.
注意: 我正在模拟 DatabaseManager class 的构造函数。检查 mockGuidelineList()
方法。如果在同一个测试中调用构造函数,则模拟方法有效 class。
我得到的异常是:
java.lang.NullPointerException: Cannot return null from a non-@Nullable @Provides method
因为我正在使用 dagger 并且我正在模拟的构造函数初始化数据库对象依赖项。
public DatabaseManager() {
MyApplication.getComponent().inject(this);
}
任何帮助将不胜感激,谢谢。
我做的一切都很好但是错过了添加 class 在 @PrepareForTest
注释中调用构造函数的地方。
所以在我的情况下,这必须是:
@PrepareForTest({JSONReader.class, ConstantsPath.class, DatabaseManager.class, SearchViewModel.class})
this Whosebug 的回答帮助了我。