New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method not mocked at partial mock #84

Closed
jOlliv opened this Issue Oct 8, 2014 · 4 comments

Comments

2 participants
@jOlliv

jOlliv commented Oct 8, 2014

In the following example code one test fails because the real method is called, although the class that declares the method is mocked. It occurs if the tested class instance is partially mocked and a sub class of the mocked class.

public abstract class AbstractClass {
    protected boolean isRendered() {
        System.out.println("### real isRendered() is called");
        return false;
    }
}

public class ImplementationClass extends AbstractClass {}

public class ImplementationClassTest {
    @Tested private ImplementationClass implementationClass;
    @Mocked private AbstractClass mockAbstractClass;

    @Test public void testWillSucceedBecauseIsRenderedIsMockedWithExpectedResultAtAbstractClass() throws Exception {
        new NonStrictExpectations() {{ mockAbstractClass.isRendered(); result = true; }};
        assertThat( implementationClass.isRendered(), is( true ));
    }

    @Test public void testWillSucceedBecauseIsRenderedIsMockedWithExpectedResultAtImplementationClass() throws Exception {
        new NonStrictExpectations( implementationClass ) {{ implementationClass.isRendered(); result = true; }};
        assertThat( implementationClass.isRendered(), is( true ));
    }

    @Test public void testWillUnexpectedlyFailBecauseTheRealIsRenderedIsCalled() throws Exception {
        // just the declaration for the partial mock let the test fail,
        // it is not necessary to record any method calls for the partial mock
        new NonStrictExpectations( implementationClass ) {{ mockAbstractClass.isRendered(); result = true; }};
        assertThat( implementationClass.isRendered(), is( true ));
    }
}

@rliesenfeld rliesenfeld added the bug label Oct 8, 2014

@rliesenfeld rliesenfeld self-assigned this Oct 8, 2014

@rliesenfeld

This comment has been minimized.

Show comment
Hide comment
@rliesenfeld

rliesenfeld Oct 8, 2014

Member

Thanks. The third test should fail with a proper validation failure; I will fix this.

Member

rliesenfeld commented Oct 8, 2014

Thanks. The third test should fail with a proper validation failure; I will fix this.

@jOlliv

This comment has been minimized.

Show comment
Hide comment
@jOlliv

jOlliv Oct 10, 2014

I don't understand why the test should fail.
After reading the jmockit tutorial I would expect that all methods of AbstractClass are mocked, because the type AbstractClass is mocked. When I have a sub class of AbstractClass then all methods declared explicitly in the sub class are not mocked but the methods inherited from AbstractClass are mocked. Now when I partial mock the sub class I would expect that additional to the mocked methods of AbstractClass I can mock some methods explicitly declared in the sub class. And consistent to that assumptions in a case where the sub class overrides a method of AbstractClass I would expect that only the method implementation of the AbstractClass is mocked until I mock the sub class as type or partial (for the method).

From the following test code I supposed that it is legal code for a jmockit test.

Please can you explain, what is wrong with my above assumptions and why the test should fail with a validation failure as you mentioned in your comment!

public class BaseClass {
    protected boolean isRendered() {
        System.out.println("real isRendered() of BaseClass called");
        return false;
    }
}

public class ImplClass extends BaseClass {
    @Override protected boolean isRendered() {
        final boolean rendered = super.isRendered();
        if (! rendered) { generateHiddenFlag(); }
        return rendered;
    }

    protected void generateHiddenFlag() {
        System.out.println("real generateHiddenFlag() called");
    }
}

public class ImplClassTest {
    @Tested private ImplClass implClass;
    @Mocked private BaseClass mockBaseClass;

    @Test public void testGeneratingNoHiddenFlagWhenRendered() throws Exception {
        new NonStrictExpectations(implClass) {{
            mockBaseClass.isRendered(); result = true;
            implClass.generateHiddenFlag(); times = 0;
        }};
        assertThat( implClass.isRendered(), is( true ));
    }

    @Test public void testGeneratingHiddenFlagWhenNotRendered() throws Exception {
        new NonStrictExpectations(implClass) {{
            mockBaseClass.isRendered(); result = false;
            implClass.generateHiddenFlag(); times = 1;
        }};
        assertThat( implClass.isRendered(), is( false ));
    }

    // some further tests that verify the behaviour of method generateHiddenFlag
}

jOlliv commented Oct 10, 2014

I don't understand why the test should fail.
After reading the jmockit tutorial I would expect that all methods of AbstractClass are mocked, because the type AbstractClass is mocked. When I have a sub class of AbstractClass then all methods declared explicitly in the sub class are not mocked but the methods inherited from AbstractClass are mocked. Now when I partial mock the sub class I would expect that additional to the mocked methods of AbstractClass I can mock some methods explicitly declared in the sub class. And consistent to that assumptions in a case where the sub class overrides a method of AbstractClass I would expect that only the method implementation of the AbstractClass is mocked until I mock the sub class as type or partial (for the method).

From the following test code I supposed that it is legal code for a jmockit test.

Please can you explain, what is wrong with my above assumptions and why the test should fail with a validation failure as you mentioned in your comment!

public class BaseClass {
    protected boolean isRendered() {
        System.out.println("real isRendered() of BaseClass called");
        return false;
    }
}

public class ImplClass extends BaseClass {
    @Override protected boolean isRendered() {
        final boolean rendered = super.isRendered();
        if (! rendered) { generateHiddenFlag(); }
        return rendered;
    }

    protected void generateHiddenFlag() {
        System.out.println("real generateHiddenFlag() called");
    }
}

public class ImplClassTest {
    @Tested private ImplClass implClass;
    @Mocked private BaseClass mockBaseClass;

    @Test public void testGeneratingNoHiddenFlagWhenRendered() throws Exception {
        new NonStrictExpectations(implClass) {{
            mockBaseClass.isRendered(); result = true;
            implClass.generateHiddenFlag(); times = 0;
        }};
        assertThat( implClass.isRendered(), is( true ));
    }

    @Test public void testGeneratingHiddenFlagWhenNotRendered() throws Exception {
        new NonStrictExpectations(implClass) {{
            mockBaseClass.isRendered(); result = false;
            implClass.generateHiddenFlag(); times = 1;
        }};
        assertThat( implClass.isRendered(), is( false ));
    }

    // some further tests that verify the behaviour of method generateHiddenFlag
}
@rliesenfeld

This comment has been minimized.

Show comment
Hide comment
@rliesenfeld

rliesenfeld Oct 10, 2014

Member

Yes, but all methods from AbstractClass do get mocked. The only problem was that in the third test, the "new NonStrictExpectations( implementationClass )" call caused JMockit to get into an inconsistent state regarding the mocking of the base class. But the real problem here is not that, but the fact that this partial mocking call shouldn't have been accepted in the first place; it should instead have triggered an "InvalidArgumentException: class AbstractClass is already mocked". This is the fix I am applying, to tighten up the validation against duplicate mockings which already is in place, so that it also checks the super-classes.

You probably want to write "@tested @Mocked ImplementationClass implementationClass". This should provide all that's needed for the tests.

Member

rliesenfeld commented Oct 10, 2014

Yes, but all methods from AbstractClass do get mocked. The only problem was that in the third test, the "new NonStrictExpectations( implementationClass )" call caused JMockit to get into an inconsistent state regarding the mocking of the base class. But the real problem here is not that, but the fact that this partial mocking call shouldn't have been accepted in the first place; it should instead have triggered an "InvalidArgumentException: class AbstractClass is already mocked". This is the fix I am applying, to tighten up the validation against duplicate mockings which already is in place, so that it also checks the super-classes.

You probably want to write "@tested @Mocked ImplementationClass implementationClass". This should provide all that's needed for the tests.

@jOlliv

This comment has been minimized.

Show comment
Hide comment
@jOlliv

jOlliv Oct 13, 2014

Thank you for the explanation! With the @tested annotation it works for me as expected.

jOlliv commented Oct 13, 2014

Thank you for the explanation! With the @tested annotation it works for me as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment