1

I have read many posts the last few days and there are some that are trying to do what I am trying to accomplish however they are on a class that is extending a class with a protected method. However mine is different.

I currently have a test class that extends and Mocks the abstract class with the protected method. No matter how I try to implement the value for the one keeps returning null and I cannot figure out why this is occurring. In turn this is returning a nullPointerEception rather then my custom exception which is should hit.

My test class is as follows:

@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");
    }
}

The abstract class:

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);
    }
}

The Class testing for the Exception:

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;
}

The abstract classes method 'testScrape' tests if the html resource contains timeout session information. I have used this method in other tests that do not use Mockito and they have all worked:

Example

@Test(expected=customException.class)
    public void scrape_TimeOut_Exception() throws Exception {
    List<CartItem> result = testScraper("htmlResource","myUrl");
}

The test should validate to true as the html resource does contain session timeout information.

The problem I believe when I debug on

return serviceScrape.scrapeString(url, html, scraper);

The scraper returns null. I have tried doing things such as

AbstractScraperTest.scraper = scraper;

as well moving the calls in the @before in abstractScraperTest into the @before in the ServiceTest class however that does not seem to work as well. I am not sure why I am returning null and cant put a value in it, I believe this is why it is failing.

Links Looked into that I remember:

  1. mocking protected method
  2. How to mock protected subclass method inherited from abstract class?
  3. How can I test a method which invoke protected (unwanted) methods of parent class?
  4. http://huahsin68.blogspot.ca/2014/01/invoke-protected-method-with.html
  5. https://groups.google.com/forum/#!topic/powermock/DtMMHa9k-4Q
  6. http://www.vogella.com/tutorials/Mockito/article.html

I did read into one post that was saying to use PowerMockito however I could not get the methods and implementations for that to work.

I am fairly sure this has to be possible for what I am trying to achieve as other ones are fairly similar however I cannot figure the correct implementation for it.

halfer
  • 19,824
  • 17
  • 99
  • 186
L1ghtk3ira
  • 3,021
  • 6
  • 31
  • 70

1 Answers1

1

serviceScrape.scrapeString(url, html, scraper) is returning null because the test is calling the mock, and there isn't any where instruction, so the default behavior is to return null.

Looking at the commented lines, I think that your goal is to use partial mocks and call the real methods by default, isn't it? The way to do this is:

@Mock(answer=Answers.CALLS_REAL_METHODS) private ServiceForScraping serviceScrape;

EDIT =======

This is the result after a discussion in the comments. I totally misunderstood the problem... Now I see what's the problem and looking at the commented lines in the method scrape_TimeOut_Exception(), my answer is this:

Mockito is intended to mock the collaborators of the class you are testing, not the test itself.

In your case, you should just implement the method getScraper() in the class 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");
    }
}

the abstract class:

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);
    }
}

It would be even better if the abstract method is not used at all:

@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");
    }
}

the abstract class:

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);
    }
}

Note that in this way, each test extending the abstract class may configure all the mocks (also the inherited ones) as needed.

I hope this helps and sorry for my misunderstanding!

Lorenzo Murrocu
  • 688
  • 4
  • 14
  • So I tried adding you partial mock (which I am not fully sure what it does) and it still returns null. Perhaps I am implementing it wrong? I updated the post with you addition. Appreciate the help by the way. – L1ghtk3ira Jun 28 '16 at 13:01
  • 1
    Also I read this post http://stackoverflow.com/questions/33516324/difference-between-mockito-spy-and-mockanswer-answers-calls-real-methods and the answer says this add you suggested is unsafe in syntax. – L1ghtk3ira Jun 28 '16 at 13:06
  • Indeed it is! However, your goal is not clear to me. Using mockito, you should have one real instance that you are testing, and the mocks for the collaborators' classes. Is the real instance you are testing the one declared in the abstract class, ServiceForScraping serviceScrape? – Lorenzo Murrocu Jun 28 '16 at 14:51
  • Yes. This class uses another method to check for a session timeout. The method runs and then checks for a session timeout where my logic is as well as my custom exception being throw. I will ad that class as well. Hope it helps you understand what is gong on. – L1ghtk3ira Jun 28 '16 at 14:53
  • It should be simpler than I initially thought: just remove the field declaration @Mock private ServiceForScraping serviceScrape and use the one declared in the abstract class. I will edit my answer to better explain what I mean. – Lorenzo Murrocu Jun 28 '16 at 14:59
  • When I sen the html resource with the url the method in AbstractScraperTest scrapes the page and runs it through the method checkSessionTimeout inside of Scraper to see if the page contains 2 classes. If they do then the page has a time out. My exception should be thrown and the test should pass since I am expecting in my test for the custom exception to be thrown. – L1ghtk3ira Jun 28 '16 at 15:00
  • I removed the mock and will edit the post to update. However I wasn't explicitly using it in the class. I am a bit confused on what you are trying to do. – L1ghtk3ira Jun 28 '16 at 15:11
  • I used commenting so you know its out but is still there in case it helps. Thanks again for helping. – L1ghtk3ira Jun 28 '16 at 15:12
  • Sorry, I couldn't try anything for a while. I think I misunderstood the problem: what you want is to get a mock for Scraper, using the abstract method getScraper(), but it returns null. Did I understand correctly this time? – Lorenzo Murrocu Jun 28 '16 at 15:27
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/115859/discussion-between-lorenzo-murrocu-and-l1ghtk3ira). – Lorenzo Murrocu Jun 28 '16 at 16:18
  • I believe so. When I debug all the values are fine except scraper that shows is null as well as serviceScrape. – L1ghtk3ira Jun 28 '16 at 16:19
  • I am working on your solution now. I believe you missed this in your scare_timeout_Exception method: AbstractScraperTest ab = Mockito.mock(AbstractScraperTest.class, Mockito.CALLS_REAL_METHODS); – L1ghtk3ira Jun 28 '16 at 17:44
  • It is still returning null. – L1ghtk3ira Jun 28 '16 at 17:51