简体   繁体   English

jUnit&Mockito-模拟“何时”触发验证

[英]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: 在我的代码中,我有一个对myInstance的调用,该调用将在执行过程中进行,因此将其存入setUp方法中:

@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"); 问题1:测试中的条件意味着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); 一种快速的解决方案是将该存根移出setUp方法,并移至该交互有效的一个测试中,这将给我Mockito.verifyZeroInteractions(myInstance);的预期结果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. 问题2:但是,如果我引入第三个也需要此存根的测试该怎么办……我宁愿不要在此新测试中重复设置存根的代码,而希望在setUp中避免它复制。 Is there a way to achieve this and still have Mockito.verifyZeroInteractions(myInstance); 有没有办法做到这一点,仍然有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: 从本质上来说,我期望的是,由于我的测试强制执行PathNotFoundException,因此对我存根的country.getProperty()的调用永远不会发生,并且调试它们不会的代码,但是可惜验证失败:

@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 ). 我没有适合您的解决方案,只有一点提示:您的测试方法应具有尽可能小的固定性(有关更多信息,请参见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. 发生错误时,很难调试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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