ssacher-tgm / mockito

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

Allow verifying no more interactions ignoring stubs #162

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
I would like EasyMock-like mode for unexpected invocations. This means if I
call methods on a mock, which were not set to return values, it will throw
exception right away. This would enable mode, when the developer is
explicitly warned (by exceptions), that he did not cover some interaction.

This could be provided through settings parameter, so I could do something
like this in my tests:

IAction action =
mock(IAction.class,withSettings().setThrowExceptionOnUnexpectedCall(true));

service.doAction(action); // throws exception, because doAction calls e.g.
action.foo()

verify(action,times(1)).run();

This mode is a safety net for the developer. Such exception means, that
developer did not cover some call.
Note, that current Mockito.verifyNoMoreInteractions() is very unhandy:
1. I have to call it explicitly at the end of the test.
2. I have to verify for invocation, I'm not really that interested in.
E.g.:
Service service = mock(Service.class);
when(service.getA()).thenReturn("b");
....invoke something, which uses Service....
Mockito.verifyNoMoreInteractions()

If I don't verify for getA(), then verifyNoMoreInteractions would through
an exception. But I don't care about getA(). I've already set return value.
I don't care, if it gets called. I care, that nothing else is called.

Thanks.
Oleg

Original issue reported on code.google.com by straz...@gmail.com on 29 Dec 2009 at 2:22

GoogleCodeExporter commented 8 years ago
I think you can implement an Answer and pass it as default answer to the mock to
achieve this.

I'll comment on your idea later, when i have some time :)

Original comment by szcze...@gmail.com on 29 Dec 2009 at 2:28

GoogleCodeExporter commented 8 years ago
Thanks. But that wouldn't work.

I tried this:

mock = mock(Dao.class,new ThrowsException(new RuntimeException("Method not 
mocked")));
and later
when(dao.addUser(ID, USERNAME)).thenReturn(new User(ID, USERNAME));

This threw exception with "Method not mocked", because:
in org.mockito.internal.MockHandler.handle(Invocation)

there is code 
        if (stubbedInvocation != null) {
            mockingProgress.getDebuggingInfo().reportUsedStub(invocationMatcher);
            stubbedInvocation.captureArgumentsFrom(invocation);
            return stubbedInvocation.answer(invocation);
        } else {
            Object ret = mockSettings.getDefaultAnswer().answer(invocation);
...
        }

This actually runs default answer and that default handler throws uncaught }!)
exception. This is strange, that would mean:
- I don't understand default answers. But I've used existing answer 
implementation...
- There is a bug in default answers in Mockito :-)

Oleg

Original comment by straz...@gmail.com on 29 Dec 2009 at 3:36

GoogleCodeExporter commented 8 years ago
actually it's not a bug but a feature xD (more like a trade off of the syntaxt).

You try doReturn() syntax... but it's not going to be great anyway.

Original comment by szcze...@gmail.com on 29 Dec 2009 at 8:37

GoogleCodeExporter commented 8 years ago
>This mode is a safety net for the developer. Such exception means, that
developer did not cover some call.

To me, the essence of unit testing is not 'covering calls' but is describing
interesting behavior. I don't want my developers to focus on covering all the
interactions. I want their tests to show meaningful examples of how the system 
under
test works. And I want the tests to drive implementation & design via TDD. The 
sooner
the developer realizes testing is not covering lines of code/if
statements/interactions the higher quality tests he will write.

I agree sometimes it is nice to detect unexpected invocations. However this 
strategy
quickly leads to less maintainable tests. Tests that break even if I introduce 
new
piece of functionality in a test-driven fashion. I may have forgotten already 
but I
cannot remember using verifyNoMoreInteractions() even once in my tests...

Your request reminds about couple of things I want to do in Mockito.

1. Allow verifying no more interactions ignoring stubs
2. Allow detecting unused stubs

Hopefully, I'll get to do it soon xD and I think some of that will address some
issues you raised.

Cheers & happy New Year! ;)

Original comment by szcze...@gmail.com on 31 Dec 2009 at 2:05

GoogleCodeExporter commented 8 years ago
>To me, the essence of unit testing is not 'covering calls'
That is not, what I wanted to say. You said it the way I feel it, so no need for
clarification :-)

I think those 2 points will cover my needs.

Thanks.

Original comment by straz...@gmail.com on 1 Jan 2010 at 10:28

GoogleCodeExporter commented 8 years ago
FYI. Some of that stuff will be released with 1.8.3 but it will be experimental 
and
probably I won't document it eagerly.

Original comment by szcze...@gmail.com on 14 Feb 2010 at 12:35

GoogleCodeExporter commented 8 years ago

Original comment by szcze...@gmail.com on 14 Feb 2010 at 9:54

GoogleCodeExporter commented 8 years ago
I would like something similar to what is requested here.

As a workaround I am using this Answer that I wrote:

public class ActivableThrowsException implements Answer<Object>
{
    private boolean active = false;

    public Object answer(InvocationOnMock invocation) throws Throwable
    {
        if (isActive())
        {
            return new ThrowsException(
                new IllegalStateException(
                    "Unexpected method call. You must define an expectation for method "
                                    + invocation.getMethod())).answer(invocation);
        }
        else
        {
            return null;
        }
    }

    public boolean isActive()
    {
        return active;
    }

    public void setActive(boolean active)
    {
        this.active = active;
    }

}

The usage is not that comfortable, though. I use it like this:

ActivableThrowsException activableThrowsException = new 
ActivableThrowsException();
Something something = mock(Something.class, activableThrowsException);
...
when(something.doGoodStuff()).thenReturn("foo");
...
activableThrowsException.setActive(true);
callTheMethodThatWillUseTheMock(something);

Original comment by epe...@gmail.com on 13 May 2010 at 8:16

GoogleCodeExporter commented 8 years ago
Interesting idea. 

1. For your reference - if you used doReturn() syntax for stubbing you could 
get rid
of this extra 'activation' logic.

2. Why don't simply throw:
new IllegalStateException("Unexpected method call..."); instead of creating 
instance
of new ThrowsException()

Original comment by szcze...@gmail.com on 16 May 2010 at 10:04

GoogleCodeExporter commented 8 years ago
1. Thanks. I am quite new to mockito and I had missed that one.

2. I did it that way because I wanted the method name to be in the exception 
message.
I know that the stack trace will be completed for me, but having the method 
name in
the message helps in identifying the method call that caused the problem in a 
line
that has several chained method calls (ie: 
a.doFoo().doBar().doFooAgain().blah())

Original comment by epe...@gmail.com on 17 May 2010 at 1:07

GoogleCodeExporter commented 8 years ago

Original comment by szcze...@gmail.com on 7 Nov 2010 at 12:47

GoogleCodeExporter commented 8 years ago
I think this issue is fixed, now, is it ?

Original comment by brice.du...@gmail.com on 24 Feb 2012 at 4:16

GoogleCodeExporter commented 8 years ago
It is... Closing...

Original comment by szcze...@gmail.com on 24 Feb 2012 at 10:00