简体   繁体   中英

jUnit & Mockito - Mocked “when” triggers verification

I'm paraphrasing my code here, but my problem is thus.

I have a mocked class:

@Mock
private MyClass myInstance;

In my code I have a call to myInstance that would be made during execution, so I stub it in a setUp method:

@Before
public void setUp() {
    Mockito.when(myInstance.doSomething("Input String")).thenReutrn("Output String");
}

During execution this can happen and in a test I can verify that, however there is a test where this interaction I have stubbed does not happen (do something is never called), so I set up a test with those conditions and my initial thought is to verify this interaction does not happen:

Mockito.verify(myInstance, times(0)).doSomething("Input String");

or

Mockito.verifyZeroInteractions(myInstance);

When I run this test these verify statements fail with a message saying (again paraphrasing) no interactions were expected - pointing to these verify statements, but interactions were found. The message points to the stub in the setup method.

Question 1: The conditions in the test mean that myInstance.doSomething("Input String"); is never called during execution, and given (at least in my head) verification is to verify what did or did not happen during execution, why do the setting up of stubs interfere with this ?

The quick solution is to move this stub out of the setUp method and into the one test where this interaction is valid, which will give me the expected result from Mockito.verifyZeroInteractions(myInstance); in the other test.

Question 2: But what if I introduce a third test that also requires this stub...... I'd prefer not to duplicate the code that sets up the stub in this new test, and would want it in setUp to avoid this duplication. Is there a way to achieve this and still have Mockito.verifyZeroInteractions(myInstance); work as I expect ?

Hope I've explained myself well enough here :)

Cheers

Scribe

EDIT: Code as requested

Class under test

public class GnerateChangeCountryList extends TagSupport implements ChangeControlConstants {

private static final long serialVersionUID = -6161465612237982393L;
private static final Logger LOGGER = LoggerFactory.getLogger(GnerateChangeCountryList.class);

private Node changeControlNode;

/**
 * {@inheritDoc}
 */
@Override
public int doStartTag() throws JspException {

    try {

        NodeIterator countriesIterator = changeControlNode.getNode(COUNTRY_NODE_NAME).getNodes();
        System.out.println("!!!!");
        List<CountryChangeBean> countryList = new ArrayList<CountryChangeBean>();
        while (countriesIterator.hasNext()) {
            System.out.println("xxxxx");
            Node country = countriesIterator.nextNode();
            // Get the properties from the nodes
            String countryLanguageCode = country.getProperty("languageCountryCode").getString();
            String countryChangeStatus = country.getProperty("chgstatus").getString();
            String countryName = country.getProperty("countryName").getString();

            String countryUserName = country.getProperty("userName").getString();
            String countryUserComment = country.getProperty("userComment").getString();

            // Need to convert the last modified date to simple date format
            // so it is handled differently
            Property lastModifiedProperty = country.getProperty("cq:lastModified");

            Calendar lastModifiedValue = lastModifiedProperty.getDate();

            SimpleDateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss");
            String formattedLastModifiedDate = df.format(lastModifiedValue.getTime());

            String showSiteValue = country.getProperty("showSite").getString();
            String showSiteString = "";

            if (!showSiteValue.equals("yes")) {
                showSiteString = "Not Shown";
            }
            countryList.add(new CountryChangeBean(countryLanguageCode, countryChangeStatus, countryName,
                    countryUserName, countryUserComment, showSiteString, formattedLastModifiedDate));
            pageContext.setAttribute("countryList", countryList);
        }
    } catch (PathNotFoundException e) {
        System.out.println("#####");
        LOGGER.error("Error looking up the .\"{}\" node for the change. Exception = {}", COUNTRY_NODE_NAME, e);
    } catch (RepositoryException e) {
        LOGGER.error("Repository exception when trying to retrieve data. Exception = {}", e);
    }
    return SKIP_BODY;
}

/**
 * Sets the node representing the change in question.
 * @param changeControlNode the change node
 */
public void setChangeControlNode(Node changeControlNode) {
    this.changeControlNode = changeControlNode;
}

}

Test Class (I've removed the other tests to try and keep this short)

Essentially what I expect is that because my test forces a PathNotFoundException, the calls to country.getProperty() that I have stubbed should never happen, and debugging the code they don't, but alas the verification fails:

@RunWith(MockitoJUnitRunner.class)
public class TestGnerateChangeCountryList implements ChangeControlConstants {

private static final String LANGUAGE_COUNTRY_CODE_PROPERTY = "languageCountryCode";
private static final String LANGUAGE_COUNTRY_CODE_VALUE = "en_gb";
private static final String CHANGE_STATUS_PROPERTY = "chgstatus";
private static final String CHANGE_STATUS_VALUE = "outstanding";
private static final String COUNTRY_NAME_PROPERTY = "countryName";
private static final String COUNTRY_NAME_VALUE = "UK";
private static final String USER_NAME_PROPERTY = "userName";
private static final String USER_NAME_VALUE = "someone";
private static final String USER_COMMENT_PROPERTY = "userComment";
private static final String USER_COMMENT_VALUE = "this is a comment";
private static final String LAST_MODIFIED_PROPERTY = "cq:lastModified";
private static final String SHOW_SITE_PROPERTY = "showSite";
private static final String SHOW_SITE_VALUE = "yes";
private static final String NOT_SHOW_SITE_VALUE = "no";

@Mock
private PageContext pageContext;

@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Node changeControlNode;

@Mock
private NodeIterator countriesIterator;

@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Node country;

@Mock
private Property lastModifiedProperty;

private GnerateChangeCountryList classUnderTest;

private Calendar lastModifiedValue; 

@Before
public void setUp() throws PathNotFoundException, RepositoryException {
    lastModifiedValue = new GregorianCalendar();
    lastModifiedValue.setTime(new Date());
    classUnderTest = new GnerateChangeCountryList();
    classUnderTest.setPageContext(pageContext);
    classUnderTest.setChangeControlNode(changeControlNode);
    Mockito.when(countriesIterator.hasNext()).thenReturn(true, false);
    Mockito.when(countriesIterator.nextNode()).thenReturn(country);

    Mockito.when(country.getProperty(LANGUAGE_COUNTRY_CODE_PROPERTY).getString()).thenReturn(LANGUAGE_COUNTRY_CODE_VALUE);
    Mockito.when(country.getProperty(CHANGE_STATUS_PROPERTY).getString()).thenReturn(CHANGE_STATUS_VALUE);
    Mockito.when(country.getProperty(COUNTRY_NAME_PROPERTY).getString()).thenReturn(COUNTRY_NAME_VALUE);
    Mockito.when(country.getProperty(USER_NAME_PROPERTY).getString()).thenReturn(USER_NAME_VALUE);
    Mockito.when(country.getProperty(USER_COMMENT_PROPERTY).getString()).thenReturn(USER_COMMENT_VALUE);

    Mockito.when(country.getProperty(LAST_MODIFIED_PROPERTY)).thenReturn(lastModifiedProperty);
    Mockito.when(lastModifiedProperty.getDate()).thenReturn(lastModifiedValue);

}



/**
 * Test when the countries node does not exist
 * 
 */
@Test
public void testWhenNoCountriesNodeFound() throws PathNotFoundException, RepositoryException, JspException {

    Mockito.when(changeControlNode.getNode(COUNTRY_NODE_NAME)).thenThrow(new PathNotFoundException());
    classUnderTest.setChangeControlNode(changeControlNode);
    int result = classUnderTest.doStartTag();
    Assert.assertEquals(TagSupport.SKIP_BODY, result);
    Mockito.verifyZeroInteractions(country);
    //Mockito.verify(country, Mockito.never()).getProperty(Mockito.anyString());
    //Mockito.verify(pageContext, Mockito.times(0)).setAttribute(Mockito.anyString(), Mockito.any(List.class));
}

}

I don't have a solution for you, but just a little tip: your test method should have the minimal possible fixture (read more at http://xunitpatterns.com/Minimal%20Fixture.html ). In your case there is a ton of mocking, which is not needed for the given test method (even more - you do not want any interactions at all). It makes it very hard to debug when error occurs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM