Mockito, verify that line not executed for some condition inside one test method

2.9k Views Asked by At

Im writing unit test using testng and mockito. Im normally practice to call one method few times inside same test method by using different values / condition to check all scenarios.

Please dont think about the logic, and design i have provided. this is just sample for clear what actually i want to make.

Review code below.

public class Human {

    private String name;
    private boolean parent;
    private List<Human> childs = new ArrayList<>();

    public String getName() {
        return name;
    }

    public boolean isParent() {
        return parent;
    }

    public void setParent(boolean parent) {
        this.parent = parent;
    }

    public void addChild(List<Human> childs) {
        this.childs = childs;
    }

    public List<Human> getChilds() {
        return childs;
    }


}

public class Validator {
    public boolean isParent(Human human) {
        if (null == human) {
            return false;
        }
        if (human.isParent()) {
            return true;
        }
        if (human.getChilds().size() > 0) {
            return true;
        }
        return false;
    }
}

Im writing test case for Validator isParent method by using mockito.

public class ValidatorTest {

public void testIsParent() throws Exception {
    Validator validator = Mockito.spy(new Validator());
    Human human = Mockito.mock(Human.class);
    Mockito.when(human.isParent()).thenReturn(false);
    boolean isParent = validator.isParent(human);
    Mockito.verify(human).getChilds();

    Mockito.when(human.isParent()).thenReturn(true);
    isParent = validator.isParent(human);
    Mockito.verify(human).getChilds();


}

In here i want to verify that getChilds() never call for second method call to validator.isParent(human) because mocked human set to return true when call human.isParent();

I used Mockito.verifyZeroInteractions() but it says fail As i understand Mockito.verifyZeroInteractions() check through all test. not only for particular method call.

I want to know is there some way to verify that method is not call for some cases and method call for same cases within same test method. Or should i should practice test one scenario in one test method.

3

There are 3 best solutions below

2
On

It's a good practice to have "one scenario per one one test method" (see How many unit tests should I write per function/method? )

Technically it's still possible to reset mocks with Mockito.reset(...), but this what official documentation says about it:

Smart Mockito users hardly use this feature because they know it could be a sign of poor tests. Normally, you don't need to reset your mocks, just create new mocks for each test method.

Instead of reset() please consider writing simple, small and focused test methods over lengthy, over-specified tests. First potential code smell is reset() in the middle of the test method. This probably means you're testing too much. Follow the whisper of your test methods: "Please keep us small & focused on single behavior".

See https://static.javadoc.io/org.mockito/mockito-core/2.9.0/org/mockito/Mockito.html#17

4
On

The verify method can accept a second argument where you can specify how many times the method has been called. You can use this to say the method was never called, called once, twice etc.

For example:

import static org.mockito.Mockito.never;

...

public void testIsParent() throws Exception {
    Validator validator = Mockito.spy(new Validator());
    Human human = Mockito.mock(Human.class);
    Mockito.when(human.isParent()).thenReturn(false);
    boolean isParent = validator.isParent(human);
    Mockito.verify(human).getChilds();

    Mockito.when(human.isParent()).thenReturn(true);
    isParent = validator.isParent(human);
    Mockito.verify(human, never()).getChilds();

}

The documentation for this is here: http://static.javadoc.io/org.mockito/mockito-core/2.9.0/org/mockito/Mockito.html#4

2
On

I want to point out that this question seriously abuses mocking, for testing something that can easily and cleanly be tested without any mocks.

This is what the tests should look like:

public class ValidatorTest {
    final Validator sut = new Validator();

    @Test
    public void checkThatNoHumanIsNotAParent() {
        boolean isParent = sut.isParent(null);

        assertFalse(isParent);
    }

    @Test
    public void checkHumanThatIsNotAParent() {
        Human notAParent = new Human();

        boolean isParent = sut.isParent(notAParent);

        assertFalse(isParent);
    }

    @Test
    public void checkParentHumanWithNoChildIsAParent() {
        Human parentWithNoChildren = new Human();
        parentWithNoChildren.setParent(true);

        boolean isParent = sut.isParent(parentWithNoChildren);

        assertTrue(isParent);
    }

    @Test
    public void checkHumanNotMarkedAsParentButWithChildIsAParent() {
        Human humanWithChildren = new Human();
        Human child = new Human();
        humanWithChildren.addChild(child);

        boolean isParent = sut.isParent(humanWithChildren);

        assertTrue(isParent);
    }
}

These tests completelly exercise all four scenarios. They are clearly much better than a version that uses mocking. Finally, note that Mockito's documentation (in the page on how to write good tests) also says that value objects (such as Human) should not be mocked.