5

I have a class (PriceSetter) that I'm testing with Mockito, and the class has an internal dependency (a database). I want to mock this internal dependency and then inject it into the class, but the dependency is not specified in my constructor. Thus, Mockito automatically tries to do constructor injection and the dependency never gets injected.

I tried using @Mock on my database object and @InjectMocks on my PriceSetter class, but Mockito automatically calls the constructor, and it fails to inject my database mock as the database is not passed into the constructor.

class PriceSetter {
    private Table priceTable;

    public PriceSetter(Dependency d1, Dependency d2) {
        this.d1 = d1;
        this.d2 = d2;
    }
}

@RunWith(MockitoJUnitRunner.class)
class PriceSetterTest{
    @InjectMocks
    private PriceSetter setter;

    @Mock Table priceTable;
    @Mock Dependency d1;
    @Mock Dependency d2;

    @Test
    public void someTestMethod() {
        when(priceTable.getItem(any())).thenReturn(Specified item);
        setter.priceTable.getItem("item"); -> Doesn't return item specified by mocked behavior
    }
}

I expect priceTable to be injected, but it isn't injected. Only d1 and d2 are injected through constructor injection.

WhoopsBing
  • 493
  • 1
  • 6
  • 12

3 Answers3

3

@InjectMocks will only do one of constructor injection or property injection, not both.

Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order...

You can always do

@Before
public void setUp() {
   setter.setPriceTable(priceTable);
}

Or however your table should get wired. However, the cleanest design is typically to unify your dependency injection method to inject everything into the constructor. Since @InjectMocks will choose the biggest constructor and work on private or package-private constructors, one option would be to add a constructor overload:

class PriceSetter {
    private Table priceTable;

    public PriceSetter(Dependency d1, Dependency d2) {
        this(d1, d2, new DefaultPriceTable());
    }

    PriceSetter(Dependency d1, Dependency d2, Table priceTable) {
        this.d1 = d1;
        this.d2 = d2;
        this.priceTable = priceTable;
    }

}
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
0

test passed for me like that :

 @RunWith(MockitoJUnitRunner.class)
public class PriceSetterTest{

    public PriceSetterTest() {}

    @InjectMocks
    private PriceSetter priceSetter;

    @Mock
    Table priceTable;

    @Mock
    Dependency1 d1;

    @Mock Dependency1 d2;

    @Test
    public void someTestMethod() {
        when(priceTable.getItem(any())).thenReturn("aa" );
        Assert.assertEquals("aa",priceTable.getItem("item"));
    }
}

you should add a default constructor(without parameters) and do priceTable.getItem("item") istead of setter.priceTable.getItem("item");

ghazouan badr
  • 472
  • 5
  • 16
0

If you use old Mockito version then fast but dirty way is to use org.mockito.internal.util.reflection.Whitebox.setInternalState to modify internal state of your tested object (it uses reflection to modify fields after constructor call).

@Test
public void someTestMethod() {
    when(priceTable.getItem(any())).thenReturn("MOCKED!");
    Whitebox.setInternalState(setter, "priceTable", priceTable);
    Assert.assertEquals("MOCKED!",  setter.priceTable.getItem("item"));
}

Downsides of this approach is 'internal' Mockito package and functionality was hidden in newer versions. Btw, there are several workarounds how to deal in this case ;).