我在过去几天看了很多帖子,有些人试图做我想要完成的事情,但是他们正在使用受保护方法扩展类的类。然而我的不同。
我目前有一个扩展的测试类,并使用受保护的方法模拟抽象类。无论我如何尝试实现一个值保持返回null,我无法弄清楚为什么会发生这种情况。反过来,这将返回一个nullPointerEception,而不是我应该命中的自定义异常。
我的测试类如下:
@RunWith(MockitoJUnitRunner.class)
public class ServiceTest extends AbstractScraperTest<HtmlPage, List<myItems>>{
@Mock private SsoSetting ssoSetting;
@Mock private SsoSetting.UrlSettings urlSetting;
// @Mock(answer = Answers.CALLS_REAL_METHODS) private ServiceForScraping serviceScrape;
@Mock Scraper scraper;
@Mock AbstractScraperTest abstractScraperTest;
@Mock private ProductScrape productScrape;
@Mock private ProductDetailScrape productDetailScrape;
@InjectMocks private Service service;
@Before
public void setUp() throws Exception {
when(ssoSetting.getUrls()).thenReturn(urlSetting);
}
final String URL = "myUrl";
final ArgumentCaptor<Map> postRequestCaptor = ArgumentCaptor.forClass(Map.class);
final ArgumentCaptor<Scraper> scrapeCaptor = ArgumentCaptor.forClass(Scraper.class);
@Test(expected = customException.class)
public void scrape_TimeOut_Exception() throws Exception {
//
// AbstractScraperTest ab = Mockito.mock(AbstractScraperTest.class, Mockito.CALLS_REAL_METHODS);
//
// assertEquals(ab.testScraper("htmlResource",
// "myUrlToTest"), customException.class);
//
// List<productItems> result = testScraper("htmlResource","myUrlToTest");
}
}
抽象类:
public abstract class AbstractScraperTest<Input, Output> {
public ServiceForScraping serviceScrape;
public Scraper<Input, Output> scraper;
protected abstract Scraper<Input, Output> getScraper();
@Before
public void setUp() throws Exception {
scraper = getScraper();
serviceScrape = new ServiceForScraping ();
ServiceForScraping .init();
}
protected Output testScraper(String filePath, String url) throws Exception {
InputStream input = getClass().getResourceAsStream(filePath);
String html = IOUtils.toString(input);
return serviceScrape.scrapeString(url, html, scraper);
}
}
异常的类测试:
public abstract class myScraper<Output> implements HTMLUnitScraper<Output> {
@Override
public Output scrape(HtmlPage page, String scraperUrl) throws Exception {
checkSessionTimeout(page, scraperUrl);
return scrapeHtmlPage(page, scraperUrl);
}
private void checkSessionTimeout(HtmlPage page, String scraperUrl) throws Exception {
if (page.getFirstByXPath("//classImLookingFor']") != null
&& page.getFirstByXPath("//ClassImLookingFor") != null) {
throw new customExceptionThrown("Session Timeout" + scraperUrl);
}
}
public abstract Output scrapeHtmlPage(HtmlPage page, String scraperUrl) throws Exception;
}
抽象类方法'testScrape'测试html资源是否包含超时会话信息。我在其他不使用Mockito的测试中使用过这种方法,并且它们都有效:
示例
@Test(expected=customException.class)
public void scrape_TimeOut_Exception() throws Exception {
List<CartItem> result = testScraper("htmlResource","myUrl");
}
测试应验证为true,因为html资源确实包含会话超时信息。
我在调试
时遇到的问题return serviceScrape.scrapeString(url, html, scraper);
scraper返回null。我尝试过像
这样的事情AbstractScraperTest.scraper = scraper;
同时将abstractScraperTest中的@before中的调用移动到ServiceTest类中的@before,但是这似乎不起作用。我不知道为什么我要返回null并且不能在其中加入值,我相信这就是它失败的原因。
链接看起来我记得:
我确实读过一篇说要使用PowerMockito的文章但是我无法获得该方法和实现。
我很确定这对于我想要达到的目标是可能的,因为其他的非常类似,但我无法为它找到正确的实现。
答案 0 :(得分:1)
serviceScrape.scrapeString(url, html, scraper)
返回null,因为测试正在调用mock,并且没有任何where
指令,因此默认行为是返回null。
查看注释行,我认为您的目标是使用部分模拟并默认调用实际方法,不是吗?这样做的方法是:
@Mock(answer=Answers.CALLS_REAL_METHODS) private ServiceForScraping serviceScrape;
编辑=======
这是评论中讨论后的结果。我完全误解了这个问题......现在我看到问题是什么,并查看方法scrape_TimeOut_Exception()
中的注释行,我的答案是这样的:
Mockito旨在嘲笑您正在测试的类的合作者,而不是测试本身。
在您的情况下,您应该在课程getScraper()
中实施方法ServiceTest
:
@RunWith(MockitoJUnitRunner.class)
public class ServiceTest extends AbstractScraperTest<HtmlPage, List<myItems>>{
@Mock private SsoSetting ssoSetting;
@Mock private SsoSetting.UrlSettings urlSetting;
// @Mock(answer = Answers.CALLS_REAL_METHODS) private ServiceForScraping serviceScrape;
@Mock Scraper scraper;
@Mock private ProductScrape productScrape;
@Mock private ProductDetailScrape productDetailScrape;
@Before
public void setUp() throws Exception {
when(ssoSetting.getUrls()).thenReturn(urlSetting);
}
@Override
protected Scraper<Input, Output> getScraper()
{
return this.scraper;
}
final String URL = "myUrl";
final ArgumentCaptor<Map> postRequestCaptor = ArgumentCaptor.forClass(Map.class);
final ArgumentCaptor<Scraper> scrapeCaptor = ArgumentCaptor.forClass(Scraper.class);
@Test(expected = customException.class)
public void scrape_TimeOut_Exception() throws Exception {
// TODO Mocks should be configured like SsoSetting
// when(this.scraper.someMethod()).thenReturn(someOutput);
// ...
assertEquals(ab.testScraper("htmlResource",
"myUrlToTest"), customException.class);
List<productItems> result = testScraper("htmlResource","myUrlToTest");
}
}
抽象类:
public abstract class AbstractScraperTest<Input, Output> {
@InjectMocks public ServiceForScraping serviceScrape;
public Scraper<Input, Output> scraper;
protected abstract Scraper<Input, Output> getScraper();
@Before
public void setUp() throws Exception {
scraper = getScraper();
serviceScrape = new ServiceForScraping ();
ServiceForScraping .init();
}
protected Output testScraper(String filePath, String url) throws Exception {
InputStream input = getClass().getResourceAsStream(filePath);
String html = IOUtils.toString(input);
return serviceScrape.scrapeString(url, html, scraper);
}
}
如果根本不使用抽象方法会更好:
@RunWith(MockitoJUnitRunner.class)
public class ServiceTest extends AbstractScraperTest<HtmlPage, List<myItems>>{
@Mock private SsoSetting ssoSetting;
@Mock private SsoSetting.UrlSettings urlSetting;
// @Mock(answer = Answers.CALLS_REAL_METHODS) private ServiceForScraping serviceScrape;
// use the one from the super @Mock Scraper scraper;
@Mock private ProductScrape productScrape;
@Mock private ProductDetailScrape productDetailScrape;
@Before
public void setUp() throws Exception {
when(ssoSetting.getUrls()).thenReturn(urlSetting);
}
final String URL = "myUrl";
final ArgumentCaptor<Map> postRequestCaptor = ArgumentCaptor.forClass(Map.class);
final ArgumentCaptor<Scraper> scrapeCaptor = ArgumentCaptor.forClass(Scraper.class);
@Test(expected = customException.class)
public void scrape_TimeOut_Exception() throws Exception {
// TODO Mocks should be configured like SsoSetting
// when(super.scraper.someMethod()).thenReturn(someOutput);
// maybe set something in the tested instance:
// super.serviceScrape.setSomething(this.something);
// ...
assertEquals(ab.testScraper("htmlResource",
"myUrlToTest"), customException.class);
List<productItems> result = testScraper("htmlResource","myUrlToTest");
}
}
抽象类:
public abstract class AbstractScraperTest<Input, Output> {
@InjectMocks public ServiceForScraping serviceScrape;
@Mock public Scraper<Input, Output> scraper;
@Before
public void setUp() throws Exception {
serviceScrape = new ServiceForScraping ();
ServiceForScraping .init();
}
protected Output testScraper(String filePath, String url) throws Exception {
InputStream input = getClass().getResourceAsStream(filePath);
String html = IOUtils.toString(input);
return serviceScrape.scrapeString(url, html, scraper);
}
}
请注意,通过这种方式,扩展抽象类的每个测试都可以根据需要配置所有模拟(也包括继承的模拟)。
我希望这对我的误解有所帮助和抱歉!