cloud1105 / mockito

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

@InjectMocks and @Spy cannot be used together when object initialized by mockito #489

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
1. use @InjectMocks and @Spy annotation on the same object A.
2. use @InjectMocks annotation on object B to inject A.
3. after initMocks(this), the reference of B within A is null.

code example:
@InjectMocks
@Spy
A a;

@InjectMocks
B b;

What is the expected output? What do you see instead?
The expect result is I have a spy object A inside B after initMocks(). But 
inside B I have A refers to null.

What version of the product are you using? On what operating system?
Mockito 1.9.5, windows 7.

Please provide any additional information below.
The following code will be working as expected:
@InjectMocks
@Spy
A a = new A();

@InjectMocks
B b;

I think the problem might be in the class SpyAnnotationEngine's method 
process(Class<?> context, Object testInstance), as it will ignore the 
initialization of field annotated by both @Spy and @InjectMocks.

Although the javadoc says "If the field is also annotated with the compatible 
@InjectMocks then the field will be ignored, The injection engine will handle 
this specific case." In my test it seems the injection engine doesn't handle 
this case after all.

Original issue reported on code.google.com by yuanju...@gmail.com on 18 Apr 2014 at 12:49

GoogleCodeExporter commented 8 years ago
Remember that mockito is not an injection framework, so it won't handle every 
case. Anyway I just wonder why the spy A would need InjectMocks, does it needs 
other mocks ? `@Spy A a;` should be enough.

If so my I would say there's something wrong in your test. For me adding 
multiple INjectMocks is confusing on the code that should be tested, because 
there's several level of mocks. And anyway if A needs collaborators for some 
reason, I wouldn't use a mockito for this object creation.

Original comment by brice.du...@gmail.com on 21 Apr 2014 at 5:19

GoogleCodeExporter commented 8 years ago
Hi guys, I report the same issue. When a field is annotated with @InjectMocks, 
if it also has a @Spy annotation, the latter is ignored.

The scenario is the following:
I want to test the class TestClass, which needs a DataFilter instance

class TestClass{
@Autowired
DataFilter filter;
}

we don't want to mock the DataFilter for many reasons, and it needs another 
DataRetrieval in order to work

class DataFilter {
@Autowired
DataRetrieval dataProvider;
}

We want to mock the DataRetrieval inside the DataFilter, so we need to annotate 
DataFilter with InjectMocks. At the same time, DataFilter is just something we 
need in order to run our test on TestClass, so DataFilter must be a @Spy as 
well.

@Mock
DataRetrieval retrieval;

@InjectMocks
@Spy
DataFilter filter;

@InjectMocks
TestClass classToBeTested;

If I annoted DataFilter with Spy only I would get TestClass with the not null 
field "filter" as excepted, but the filter is not able to work because it 
doesn't have any DataRetrieval
If I annoted DataFilter with InjectMocks only I would a working instance of the 
filter, and the TestClass with filter=null

If I put both annotations instead, surprisingly, I have the same outcome of the 
lonely InjectMocks annotation

thanks for your attention

Original comment by fernando...@gmail.com on 21 May 2014 at 5:20

GoogleCodeExporter commented 8 years ago
This would require mockito to build a graph of dependencies. And this is the 
work of a dependency injection framework which mockito isn't and shouldn't 
become.

If you are desperate to support such cases, please make a pull request so we 
can evaluate the feature.

Thanks for your interest.

Cheers,
Brice

Original comment by brice.du...@gmail.com on 27 Jun 2014 at 4:32

GoogleCodeExporter commented 8 years ago
I'd like to vote for this feature.  One of my current tests is breaking because 
of this not working.  Basically, I'm testing a class A with a whole load of 
dependencies B, C, D; and B also has dependencies E, F etc.  I need to stub 
just one method of B, but otherwise have B working as normal.

So I want to mock C, D, E, F and spy B.  I then want to inject E and F into B, 
and then inject B, C, D into A.  So B requires @InjectMocks and @Spy.  But this 
seems to be explicitly disabled by a line in SpyAnnotationEngine, that says if 
(field.isAnnotationPresent(Spy.class) && 
!field.isAnnotationPresent(InjectMocks.class)) {.

I'm not sure what the reason for this is.  It seems an odd thing to disable.  
This is NOT about Mockito potentially becoming a dependency injection framework 
- it's about being able to mock and spy what we need to mock and spy.

What would be the impact of changing the offending line in SpyAnnotationEngine 
to if (field.isAnnotationPresent(Spy.class)) { ? 

Original comment by dmwallace.nz on 17 Dec 2014 at 9:55

GoogleCodeExporter commented 8 years ago
Actually, @Spy and @InjectMocks worked toghether in previous mockito release, 
i.e. 1.8.5
so this restriction comes with 1.9 or 1.10 major release, does it??

Original comment by grodecki.artur on 23 Jan 2015 at 12:58

GoogleCodeExporter commented 8 years ago
Use powermock's Whitebox.setInternalState as a workaround

Original comment by liqiang on 29 Jan 2015 at 6:25

GoogleCodeExporter commented 8 years ago
tl;dr Not gonna happen, what is being asked is actually the beginning of an 
real dependency injection library, which is not the spirit of @InjectMocks, if 
for some reason a bad design imposes to have two level of injections in the 
test, then :

1. create a dedicated factory method / builder in the test utils
2. good design can be measured by a low PITA metric, if the PITA is too high 
then a refactoring is needed
3. this may not be a unit test at all, but an integration test in which case 
why would we want mockito short hand mock injection as part of it

Original comment by brice.du...@gmail.com on 14 Feb 2015 at 8:12

GoogleCodeExporter commented 8 years ago
Checking the codebase I found a test case 
WrongSetOfAnnotationsTest.shouldNotAllowSpyAndInjectMock() which, I guess, was 
supposed to cover Brice's explanation. The test passes because the 
MockitoException is thrown, but it's thrown for different reason: the test 
tries to instantiate an interface (List). The test was there at list since 
2010, so I think the behavior could have been basically forgotten.
So, although I find Brice's explanation convincing, I still think the exception 
should be thrown in that case, informing of wrong Mockito usage.

Original comment by mail.mac...@gmail.com on 15 Feb 2015 at 8:33

GoogleCodeExporter commented 8 years ago
I let myself to raise another issue for above: 
https://github.com/mockito/mockito/issues/169

Original comment by mail.mac...@gmail.com on 15 Feb 2015 at 9:05

GoogleCodeExporter commented 8 years ago
Thx, let's continue the discussion on the PR then, it's much more comfortable 
on GH ;)

Original comment by brice.du...@gmail.com on 16 Feb 2015 at 12:42