Mockito: Cannot instantiate @InjectMocks field: the type is an interface
Anyone who has used Mockito for mocking and stubbing Java classes, probably is familiar with the InjectMocks-annotation. Use this annotation on your class under test and Mockito will try to inject mocks either by constructor injection, setter injection, or property injection. This magic succeeds, it fails silently or a MockitoException
is thrown.
I’d like to explain what causes the “MockitoException: Cannot instantiate @InjectMocks field named xxx! Cause: the type is an interface” and how to solve it.
Problem
Consider the following JUnit 5 test which verifies whether a waitress can properly serve breakfast. Anyone of the kitchen staff can serve breakfast, and the test verifies that when breakfast is served the coffee machine starts brewing coffee and the toaster starts toasting.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | @ExtendWith (MockitoExtension. class ) public class WaitressTest { @Mock CoffeeMachine coffeeMachine; @Mock Toaster toaster; @InjectMocks KitchenStaff waitress; @Test void should_serve_breakfast() { waitress.serve(BREAKFAST); verify(coffeeMachine).brew(); verify(toaster).toast(); } } interface KitchenStaff { void serve(MealType mealType); } class Waitress implements KitchenStaff { CoffeeMachine coffeeMachine; Toaster toaster; //... @Override public void serve(MealType mealType) { coffeeMachine.brew(); toaster.toast(); } } |
The collaborating coffeeMachine
and toaster
are mocked by Mockito for the purpose of this test — hence they need the Mock
annotation — so we can verify if the expected methods are invoked. The waitress is the real deal, she is being tested. By putting @InjectMocks
on her, Mockito creates an instance and passes in both collaborators — and then our actual @Test
-annotated method is called.
Unfortunately it fails: as soon as you run the test, Mockito throws a runtime exception: “Cannot instantiate @InjectMocks field named ‘waitress’! Cause: the type ‘KitchenStaff’ is an interface.”
Cause
Luckily Mockito’s error messaging has improved lately and it cleary states what’s wrong: the type KitchenStaff
is an interface.
- We have an interface.123
interface
KitchenStaff {
void
serve(MealType mealType);
}
- We say to Mockito: “instantiate this interface” (What?)12
@InjectMocks
KitchenStaff waitress
- Hey, that can’t be right!
You can not use @InjectMocks
on just the interface alone, because Mockito needs to know what concrete class to instantiate.
Remember that the unit you’re (unit) testing is one of the few lucky ones which usually are real. The KitchenStaff
is just a behavioural contract, the Waitress
is actually getting paid to serve breakfast.
Solution
There are a few, just as with using abstract classes, but it boils down to: provide a concrete type at instance declaration.
Give Mockito the class that implements the interface.
A) Declare a concrete type
Use a concrete implementation for the type of the @InjectMocks
field.
1 2 | @InjectMocks Waitress waitress; |
B) Assign a concrete type
Keep using the interface’s type for the @InjectMocks
field, but initialize it with a concrete implementation.
1 2 | @InjectMocks KitchenStaff waitress = new Waitress() |
Or of course use the concrete type in the declaration and initialization, sure, that works too 😉
However…
However, does your class under test expects (required) collaborators as arguments to a constructor?
(I sure hope so!)
E.g. consider the following single constructor:
1 2 3 4 5 6 7 8 9 | class Waitress implements KitchenStaff { final CoffeeMachine coffeeMachine; final Toaster toaster; Waitress(CoffeeMachine coffeeMachine, Toaster toaster) { this .coffeeMachine = coffeeMachine; this .toaster = toaster; } |
Then, in the absence of a no-args constructor, the compiler would tell you to call the proper constructor and provide the arguments right there and now.
1 2 3 4 | @InjectMocks KitchenStaff waitress = new Waitress() ^^ compiler error |
A. Solve it by providing the arguments yourself.
1 2 3 | // compiles again @InjectMocks KitchenStaff waitress = new Waitress(coffeeMachine, toaster); |
B. Remove @InjectMocks
. You don’t need it anymore.
1 2 3 4 5 6 | KitchenStaff waitress; @BeforeEach void setup() { waitress = new Waitress(coffeeMachine, toaster); } |
Conclusion
Trust the waitress to make you some fine toast.
Published on Java Code Geeks with permission by Ted Vinke, partner at our JCG program. See the original article here: Mockito: Cannot instantiate @InjectMocks field: the type is an interface Opinions expressed by Java Code Geeks contributors are their own. |