projoy/mockito

Mockito should provide a mechanism for classes to opt out of mocking: @Unmockable

GoogleCodeExporter opened this issue · 17 comments

Mockito is easy to use, so people use it!  Unfortunately, this means that 
sometimes they ignore the helpful advice on the mockito docs, namely:

"
Remember

Do not mock types you don't own
Don't mock value objects
Don't mock everything
"

In particular, over mocking can occasionally lead to overly brittle tests that 
also increase compatibility burdens for library owners.  Also, some classes 
shouldn't be mocked because there are better test friendly ways to create 
instances.

I propose that mockito implement a mechanism whereby types can opt-out of 
mocking via a runtime annotation.  Specifically, if a class (or super class) is 
annotated with @Unmockable (name tbd) then mockito should refuse to mock it.  
Mockito could own an instance of this annotation (org.mockito.Unmockable) but 
also support understanding Unmockable annotations from other packages.  This 
will allow library owners to provide this signal to mockito without taking on a 
mockito dependency. (like how guice handles @Nullable annotations).

Original issue reported on code.google.com by LukeISan...@gmail.com on 1 Aug 2014 at 12:52

I'm not so sure about this.  

Firstly, I fear that if we implement this, the very next feature request will 
be for a way of overriding the @Unmockable annotation.  

Secondly, the classes that really should be unmockable are things in third 
party libraries, and these shouldn't really have a dependency on a Mockito 
annotation.  It strikes me that this annotation, if it were to exist, should be 
a JDK thing, not a Mockito thing.

I would like to vote against this suggestion.

Original comment by dmwallace.nz on 1 Aug 2014 at 9:44

I disliked the idea as soon as I heard it.

The guideline of reducing mocking is nice in theoretical terms, but is 
unreasonable as an absolute restriction, for any class.

Original comment by davidmic...@gmail.com on 1 Aug 2014 at 2:26

Regarding the issue of this annotation living in mockito or elsewhere:
I think, if mockito were to support this system, it should respect any 
@Unmockable annotation (by matching on the simple name of the annotation 
class).  For example, guice respects any @Nullable annotation no matter what 
package it is in.  This allows libraries to use Guice and @Nullable without 
taking on a jsr305 dependency

The proximate motivation for this request is that a project i maintain has 
recently been using @AutoValue 
(https://github.com/google/auto/tree/master/value) to author value types in our 
library.  @AutoValue has a lot of benefits, but one of the downsides is that it 
forces you to write your value types as non-final classes which opens them up 
to mocking.  Value Types should definitely never be mocked, test authors should 
just construct real instances instead.

Another issue I have had a lot of trouble with is users mocking guavas 
ListenableFuture 
(https://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained) 
interface.  I have fixed dozens of tests that were mocking ListenableFuture to 
instead use the various future factory methods that guava provides already.  
These tests have always been shorter and easier to read after removing the 
mocks.

So I disagree that this would introduce an 'unreasonable' restriction.  If a 
user has a use case for mocking a class that is marked @Unmockable, then they 
should raise that with the library owners.  The library authors could then work 
with them to provide either better testing utilities or to remove the 
annotation.

Original comment by LukeISan...@gmail.com on 1 Aug 2014 at 3:08

...to provide a little more context on the listenablefuture issue, The reason i 
was fixing these tests is because the existence of these ListenableFuture mocks 
blocked (for many many months) some basic performance enhancements to guavas 
future based utilities.  Mockito made it far too easy to make a 
ListenableFuture instance that violated the contract.

Original comment by LukeISan...@gmail.com on 1 Aug 2014 at 4:13

I would also like to see this feature implemented.  In many cases people grab 
for Mockito because it's the tool they know and never find any of the more 
appropriate fakes because they're too busy swinging the mocking hammer at every 
nail, screw and turnip they find.  To that end, if @Unmockable had a String 
description that could be used describe why they shouldn't be mocking that type 
and what alternatives were available for testing, it would go a long way 
towards improving test quality.

Original comment by gk5...@gmail.com on 1 Aug 2014 at 4:27

Hi,
I believe this is achievable with something like that in mockito if you provide 
your own mockmaker. The custom implementation wil check for criterias to mock 
or to not mock. If the type is mockable then it can delegate to the default 
Mockito MockMaker.

Having said that I do not lean in this approach, and I do not support an an 
implementation from within Mockito.

I will tend to close this issue as won't fix. Any one against it ?

Brice

Original comment by brice.du...@gmail.com on 1 Aug 2014 at 5:07

  • Added labels: Priority-Low, Type-Enhancement
  • Removed labels: Priority-Medium, Type-Defect
Instead of trying to make the class unmockable, put in the class description 
the recommended way of testing with the class.

Original comment by Daniel.Cardenas@gmail.com on 1 Aug 2014 at 5:39

That is an approach that I have considered, but it doesn't help in situations 
like Guava where you publish a project that is then used by third party 
developers.  There would be no reasonable mechanism to get those third_parties 
to use the MockMaker in question (the service locator pattern used is too 
inflexible to allow multiple mock makers to be registered).

And even if that could be resolved by making the mockmaker locator more 
flexible (say by allowing mock makers to be registered for particular types), 
that would definitely force  libraries like guava to take on Mockito 
dependencies which is probably not appropriate.

Original comment by LukeISan...@gmail.com on 1 Aug 2014 at 5:43

Comments aren't an effective mechanism.  Mockito.spy has lots of comments about 
when it is or is not appropriate, no one reads it, or rather, it's not the 
users who read the docs that you need to help, it's the users who don't read 
the docs who need to be stopped.

Original comment by LukeISan...@gmail.com on 1 Aug 2014 at 5:48

Fair point

Original comment by brice.du...@gmail.com on 1 Aug 2014 at 6:16

Having reread the comments, I understand better the motivation and I've still 
mixed feelings about this.

Original comment by brice.du...@gmail.com on 1 Aug 2014 at 6:22

The reality is, many developers are asked to write unit tests for existing 
code, without the ability to refactor that code.  My very large multinational 
company is one (although I don't know how much this happens within the company).

The first time one of those developers is hit by this, they're not going to ask 
for advice for convincing their management to allow them to refactor this code, 
they're going to ask how to turn this off.

It's reasonable to recommend guidelines for writing good code, but it's not 
reasonable to distrust the developer enough to try to block them from doing 
something you don't recommend.  That is not agile.

Original comment by davidmic...@gmail.com on 1 Aug 2014 at 6:44

One of my fears is that people will overuse the @Unmockable annotation, no 
matter which library it lives in.  Then you'll get a whole lot of situations 
where developers reason like this.  "I need to mock XYZ, but it's Unmockable, 
so I can't use Mockito.  I'll use Easymock just for these tests, because it 
doesn't understand Unmockable".  Then people end up with two mocking libraries 
in their project, and everything becomes a real mess.

Original comment by dmwallace.nz on 1 Aug 2014 at 6:46

[deleted comment]
Well currently mockito doesn't mock private or non-final classes (though 
technically, Mockito.spy can spy on private classes).  So this would really 
just be a more flexible extension of that system. 

I think we all agree that there are types users shouldn't mock (String, List, 
Set, Map...). I also understand that there is an inherent trade-off in the 
flexibility mockito provides to library authors and test authors (my proposal 
gives more flexibility to library authors).  Obviously, users can mark things 
Unmockable when they shouldn't, just like users can mock things when they 
shouldn't.  In both those cases, the real issues come up due to a lack of 
communication between library authors and test authors.  I think this proposal 
would help to improve that communication (and thus also, testing in general).

FYI: if this proposal is accepted, I will take it up with EasyMock also.  
Mockito is just more popular/better so I'm starting here.

Original comment by LukeISan...@gmail.com on 1 Aug 2014 at 6:58

Perhaps a better and more manageable approach would be to allow tests to define 
an interceptor (even at the annotation level), which would be passed 
information about the mock request.  This would even allow you to throw an 
exception if you tried to mock List, Set, or Map, in addition to user types.

Obviously, this would require tests to use the interceptor defined by their 
organization.  That's what trust means.  It would also allow developers to 
override that interceptor for special cases.

Original comment by davidmic...@gmail.com on 1 Aug 2014 at 7:59

davidmic...@: that option is currently available via the custom MockMaker, but 
it doesn't solve my problem of libraries being able to steer users towards more 
appropriate testing utilities.  (of course my solution won't solve the problem 
of users mocking List either,... mockito should probably just implement that 
anyway, but that is a separate bug).

Original comment by LukeISan...@gmail.com on 2 Aug 2014 at 1:11