easyforgood / mockito

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

Mocking final properties using constructor injection 1.9.0 has odd behaviour. #309

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
Using Mockito 1.9.0, in the following example, using final for property myDep 
has a confusing effect.

Whether we use final (lines 2,3) or not, myDep is injected by Mockito and the 
NullPointerException is never thrown.

If we use final, the output from this test is:
myDep1 = myDep
myDep2 = myDep

If we remove final, the output is:

myDep1 = myDep
myDep2 = myDep
doSomethingMocked

It appears that the mock injected into a final method is not performing 
correctly.

I am aware that the documentation 
(http://docs.mockito.googlecode.com/hg/latest/org/mockito/InjectMocks.html) 
says that final properties are ignored - but in this instance it appears that 
something is being injected, it's just not providing mocking facilities.

1 public class MyClass {
2     MyDep myDep;
3 //    final MyDep myDep; 
4 
    @Autowired
    public MyClass(MyDep myDep) {
        if (myDep == null) throw new NullPointerException();
        System.out.println("myDep1 = " + myDep);
        this.myDep = myDep;
    }

    public void go() {
        System.out.println("myDep2 = " + myDep);
        myDep.doSomething();
    }
}

---
public class MyDep {
    public String doSomething() {
        System.out.println("doSomething");
        return "bar";
    }
}
----
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    MyDep myDep;

    @InjectMocks
    MyClass myClass;

    @Test
    public void doTest() {
        MockitoAnnotations.initMocks(this);

        when(myDep.doSomething()).thenAnswer(new Answer<String>() {
            @Override
            public String answer(InvocationOnMock invocationOnMock) throws Throwable {
                System.out.println("doSomethingMocked");
                return "foo";
            }
        });
        myClass.go();
    }
}

Regards, Tom

Original issue reported on code.google.com by tommytastic on 13 Jan 2012 at 10:55

GoogleCodeExporter commented 8 years ago
Hi Tom

There's nothing wrong here with the injection. You are initializing your mocks 
two times. One with the runner and one with MockitoAnnotations.
Just use only one.

For information here's what happens :
Case A : non final field
1. MockitoJUnitRunner creates the mock "myDep", and the instance myClass using 
constructor param "myDep"
2. MockitoAnnotations.init() creates a new mock "myDep", and sees that myClass 
is already instanciated, so it tries setter injection AND field injection, the 
new mock instance is injected.
3. The mock instance is the same in myDep field in test class and in myDep 
field in production class, so when stubbing you are talking to the correct mock 
instance.

Case B : final field
1. MockitoJUnitRunner creates the mock "myDep", and the instance myClass using 
constructor param "myDep"
2. MockitoAnnotations.init() creates a new mock "myDep", and sees that myClass 
is already instanciated, so it tries setter injection and field injection, BUT 
field is final, the new mock instance is NOT injected.
3. The mock instance is NOT the same in myDep field in test class and in myDep 
field in production class, so when stubbing you are talking to the mock 
instance in test class, not the instance in the production class.

Original comment by brice.du...@gmail.com on 13 Jan 2012 at 12:09

GoogleCodeExporter commented 8 years ago
Ah right.  I see. If I remove one of them, my Camel ProducerTemplate is not 
getting injected into @Produce. This is probably a separate problem though. 
I'll investigate.

Original comment by tommytastic on 17 Jan 2012 at 1:59

GoogleCodeExporter commented 8 years ago

Original comment by brice.du...@gmail.com on 3 Sep 2012 at 10:00