Skip to content
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

whenNew not matching constructor when null is passed as first or last argument. #763

Closed
podarsmarty opened this issue Mar 14, 2017 · 1 comment

Comments

@podarsmarty
Copy link
Contributor

Is this something you can debug and fix?

I think the bug is in MockGateway.java it blindly assumes first or last arguments when null are due to null for some reason seeming like it is passed in as an argument when it is not. Because it does this it strips the argument and will no longer match constructor for whenNew. In order to work around this my thought is to attempt finding a constructor with it stripped and with it not and then using the one that actually matches though this may cause issues with edgecase like when you have multiple constructors which will need to be fixed.

In order to help us to clarify issue can you answer the following question:

What steps will reproduce the problem?

  • Run my tests
package com.broken.example;


public class OuterClass {

    public InnerClass tester(boolean isFirstNull, boolean isLastNull) {
        InnerClass innerClassInstance = new InnerClass(isFirstNull ? null : "1", "2", isLastNull ? null : "3" );
        return innerClassInstance;
    }

    static class InnerClass {
        String mOne;
        String mTwo;
        String mThree;

        InnerClass(String one, String two, String three) {
            mOne = one;
            mTwo = two;
            mThree = three;
        }

        public String getOne() {
            return mOne;
        }

        public String getTwo() {
            return mTwo;
        }

        public String getThree() {
            return mThree;
        }
    }
}

Test Class:

package com.broken.example;


import com.broken.example.OuterClass.InnerClass;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static junit.framework.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.spy;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;

@RunWith(PowerMockRunner.class)
@PrepareForTest({
    OuterClass.class
})
public class NullTest {

    private OuterClass mOuterClassInstance;

    @Before
    public void setUp() throws Exception {
        mOuterClassInstance = spy(new OuterClass());
    }

    @Test
    public void test_InnerConstructor_IsMocked() throws Exception {
        InnerClass mockInnerClass = mock(InnerClass.class);
        when(mockInnerClass.getOne()).thenReturn("one");
        when(mockInnerClass.getTwo()).thenReturn(", two");
        when(mockInnerClass.getThree()).thenReturn(", three");

        whenNew(InnerClass.class)
                .withAnyArguments()
                .thenReturn(mockInnerClass);

        InnerClass output = mOuterClassInstance.tester(false,false);

        assertNotNull(output);
    }

    @Test
    public void test_InnerConstructorLastArgumentNull_IsIncorrectlyNotMocked() throws Exception {
        InnerClass mockInnerClass = mock(InnerClass.class);
        when(mockInnerClass.getOne()).thenReturn("one");
        when(mockInnerClass.getTwo()).thenReturn(", two");
        when(mockInnerClass.getThree()).thenReturn(", three");

        whenNew(InnerClass.class)
                .withAnyArguments()
                .thenReturn(mockInnerClass);

        InnerClass output = mOuterClassInstance.tester(false, true);

        // This should NOT be null but it is since the mock fails
        assertNull(output);
    }

    @Test
    public void test_InnerConstructorFirstArgumentNull_IsIncorrectlyNotMocked() throws Exception {
        InnerClass mockInnerClass = mock(InnerClass.class);
        when(mockInnerClass.getOne()).thenReturn("one");
        when(mockInnerClass.getTwo()).thenReturn(", two");
        when(mockInnerClass.getThree()).thenReturn(", three");

        whenNew(InnerClass.class)
                .withAnyArguments()
                .thenReturn(mockInnerClass);

        InnerClass output = mOuterClassInstance.tester(true, false);

        // This should NOT be null but it is since the mock fails
        assertNull(output);
    }
}

What is the expected output?

  • 2 of my 3 tests should fail.

What do you see instead?

  • They pass.

What version of the product are you using?

  • 1.6.5

On what operating system?

  • macOS Sierra 10.12.3
@podarsmarty
Copy link
Contributor Author

podarsmarty commented Mar 15, 2017

Interesting, the comment about having a null seems to only apply for PowerMock and not PowerMockito.

edit: i lied it is just not easily apparent in PowerMockito because of withAnyArguments()

edit2: hmm test seems to be flaky. Was failing all the time earlier but now passes just fine. Earlier the comment about null being passed in was not happening but now it is.
edit3: seems like it works fine if the inner class' constructor is private due to null being passed in but fails when it is public, protected, or package-protected due the the null not being passed in.

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

No branches or pull requests

2 participants