spat1978 / mockito

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

@InjectMocks has inconsistent behavior when the test class extends another class #454

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
Consider the following class and test:

public class Sample {

    Object constructorAttribute;
    Object propertyAttribute;
    Object fieldAttribute;

    public Sample(Object constructorAttribute) {
        this.constructorAttribute = constructorAttribute;
    }

    public void setPropertyAttribute(Object propertyAttribute) {
        this.propertyAttribute = propertyAttribute;
    }
}

@RunWith(MockitoJUnitRunner.class)
public class SampleTest {

    @Mock
    Object fieldAttribute;
    @Mock
    Object constructorAttribute;
    @Mock
    Object propertyAttribute;

    @InjectMocks
    // @Spy
    Sample injectionSample;

    @Test
    public void constructor() {
        assertEquals(constructorAttribute, injectionSample.constructorAttribute);
    }

    @Test
    public void setter() {
        // verify(injectionSample).setPropertyAttribute(propertyAttribute);
        assertEquals(propertyAttribute, injectionSample.propertyAttribute);
    }

    @Test
    public void field() {
        assertEquals(fieldAttribute, injectionSample.fieldAttribute);
    }
}

This is a standard use case for @InjectMocks. As stated in 
http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/InjectMocks.html only 
the
constuctor injection is used. In the object injectionSample, the 
propertyAttribute and fieldAttribute are null while the constructorAttribute 
receives a mock. 
The setter test and the field test fail as expected. There is a problem with 
the constructor test which can fail because the parameter resolution is made by 
type. Since all the mocks have the same type, Object in this case, mockito 
takes one of them and use it in constructor injection. It makes the constructor 
test depend on the order in which mocks and attributes are defined.

Now please consider the following test:

@RunWith(MockitoJUnitRunner.class)
public class SuperSampleTest extends BaseTest {

    @Mock
    Object constructorAttribute;

    @InjectMocks
    Sample injectionSample;

    @Test
    public void constructor() {
        assertEquals(constructorAttribute, injectionSample.constructorAttribute);
    }

    @Test
    public void setter() {
        assertEquals(propertyAttribute, injectionSample.propertyAttribute);
    }

    @Test
    public void field() {
        assertEquals(fieldAttribute, injectionSample.fieldAttribute);
    }
}

public class BaseTest {

    @Mock
    Object propertyAttribute;
    @Mock
    Object fieldAttribute;
}

It does the same tests as in the first case with the difference that some mocks 
are declared in a base test class. The surprise is that all the tests pass. All 
the attributes of the object injectionSample are mocked. In this case Mockito 
does all the injections: the constructor, the property and the field injection. 
It is inconsistent with the docs and the first test case. 

Another issue with the documentation is the description of how the injection 
algorithm works. Only one of the three injection types should happen when 
@InjectMocks annotation is used. It lacks an explanation of why. In practice, 
when the annotation is used for setter and/or field injection it seems to make 
no exclusion between this two types of injection. Both of them are applied. 
Some clarification would help.

The tests are done with mockito 1.9.5 on win 7 64 bit, jdk  1.7.0-13 64 bit. 

Let me know if I can be of any further help.

Original issue reported on code.google.com by lpet...@gmail.com on 27 Sep 2013 at 3:17

Attachments:

GoogleCodeExporter commented 8 years ago

Original comment by brice.du...@gmail.com on 4 Dec 2013 at 2:55