ssacher-tgm / mockito

Automatically exported from code.google.com/p/mockito
0 stars 0 forks source link

invocation.getArguments() produces null arguments #121

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
We use when().thenAnswer() to stub a method.  All but one of our tests
require the same stub behaviour so we placed the when().thenAnswer() into a
method as shown here,

void setupSession(final boolean fail)
{
    when(...).thenAnswer(new Answer()
    {
        public Object answer(InvocationOnMock invocation)
        {
            if (fail)
                createListener.notifyFailure(userTag, code);
            else
                createListener.notifySuccess(userTag, iterator, code);
            return request;
        }
    });
}

This worked fine under 1.7 but we've just upgraded to 1.8.0 and now see 
the null args problem.  I've provided the stripped down code below but here
is the scenario.

We called the setupSession method to do the "when().thenAnswer()" from
setUp so it's called once for every test, however we call it a second time
inside one of the tests.

On the second call we get the null arguments.  Bear in mind that we haven't
actually tried to call the offending method from the unit test.

If we replace the "when().thenAnswer()" with "doAnswer().when()" all the
test passes.

Here is the stripped down code to show the various scenarios so you can see
the issue.

We're using Java 1.4, JUnit 1.7.1, Mockito 1.8.0

package picasso.utils.retriever;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import junit.framework.TestCase;

import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class ReproduceProblemTest extends TestCase
{
    class MyClass
    {
        public String getName(String name)
        {
            return name;
        }
    }

    MyClass myClassMock = mock(MyClass.class);

    public void testWorks()
    {
        setupSessionWorks();
        setupSessionWorks();
    }

    public void testBroken()
    {
        setupSessionBroken();
        setupSessionBroken();
    }

    public void testBrokenWorks()
    {
        setupSessionBroken();
        setupSessionWorks();
    }

    public void testWorksBroken()
    {
        setupSessionWorks();
        setupSessionBroken();
    }

    void setupSessionWorks()
    {
        doAnswer(new Answer()
        {
            public Object answer(InvocationOnMock invocation)
            {
                assertNotNull(invocation.getArguments()[0]);
                return null;
            }
        }).when(myClassMock).getName(any(String.class));
    }

    void setupSessionBroken()
    {
        when(myClassMock.getName(any(String.class))).thenAnswer(new Answer()
        {
            public Object answer(InvocationOnMock invocation)
            {
                assertNotNull(invocation.getArguments()[0]);
                return null;
            }
        });
    }
}

Original issue reported on code.google.com by leemil...@gmail.com on 9 Sep 2009 at 11:03

GoogleCodeExporter commented 8 years ago
Hi,

This is working as designed and I do believe it worked the same way in 1.7 (am I
wrong ? :)

You have to know that:

1. There is a difference between doReturn() and when(). when() actually invokes 
the
method so any previous stubbing will be activated at the moment you do when(). 
This
is a trade off of the mocking syntax Mockito uses.

2. All argument matchers (like any()) always return nulls. Therefore in this 
case:

setupSessionWorks();
setupSessionBroken();

invocation.getArguments()[0] is null

If you guys are doing some complex stubbing I suggest to use doReturn() as it 
is a
tiny bit safer (but less readable...)

Original comment by szcze...@gmail.com on 12 Sep 2009 at 6:04

GoogleCodeExporter commented 8 years ago
Hi, I am able to reproduce it and I'm working on it.

Original comment by szcze...@gmail.com on 15 Sep 2009 at 8:38

GoogleCodeExporter commented 8 years ago
I see now. It's all because we changed the behavior of any() matcher in release 
1.8.0
(see issue 73)

So, generally those nulls will be there. In order to avoid NPEs, either use
doReturn/doAnswer or use isA() matcher instead of any() matcher. 

You're probably curious why the matcher matters, right? OK, so isA() matcher 
does not
allow nulls. Calling any matcher creation methods (like any()) will return 
null. This
all together makes mockito not use the previous stubbing when you call when() 
for the
second time. It's hard to explain but those are hardcore gotchas pretty much 
related
to how the mocking syntax is implemented.

Original comment by szcze...@gmail.com on 15 Sep 2009 at 9:11

GoogleCodeExporter commented 8 years ago

Original comment by szcze...@gmail.com on 20 Oct 2009 at 6:47