Enterprise Java

Mockito: Cannot instantiate @InjectMocks field: the type is an abstract class

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 abstract class” 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
40
41
@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();
  }
}
 
abstract class KitchenStaff {
  protected final CoffeeMachine coffeeMachine;
  protected final Toaster toaster;
 
  // ...
 
  abstract void serve(MealType mealType);
}
 
class Waitress extends KitchenStaff {
 
  // ...
 
  @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 abstract class.”

Cause

Luckily Mockito’s error messaging has improved lately and it cleary states what’s wrong: the type KitchenStaff is an abstract class.

  1. We have an abstract class.
    1
    2
    3
    4
    5
    6
    7
    8
    abstract class KitchenStaff {
      protected final CoffeeMachine coffeeMachine;
      protected final Toaster toaster;
     
      // ...
     
      abstract void serve(MealType mealType);
    }
  2. We say to Mockito: “instantiate this abstract class” (What?)
    1
    2
    @InjectMocks
    KitchenStaff waitress

  3. Hey, that can’t be right!

You can not use @InjectMocks on just the abstract class alone, because Mockito needs to know what subclass 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 too abstract, it knows how to hold on to a coffee machine and toaster, but the Waitress actually knows how to operate them.

Solution

There are a few, just as with using interfaces, but it boils down to: provide a concrete subclass at instance declaration.

Give Mockito the subclass that extends the abstract parent class.

A) Declare a concrete subclass

Use a subclass for the type of the @InjectMocks field.

1
2
@InjectMocks
Waitress waitress;

B) Assign a concrete subclass

Keep using the parent’s type for the @InjectMocks field, but initialize it with a concrete subclass.

1
2
@InjectMocks
KitchenStaff waitress = new Waitress()

Or of course use the subclass in the declaration and initialization, 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 in the abstract class:

1
2
3
4
5
6
7
8
abstract class KitchenStaff {
  protected final CoffeeMachine coffeeMachine;
  protected final Toaster toaster;
 
  KitchenStaff(CoffeeMachine coffeeMachine, Toaster toaster) {
    this.coffeeMachine = coffeeMachine;
    this.toaster = toaster;
  }

and the subclass needing to call the parent constructor:

1
2
3
4
5
class Waitress extends KitchenStaff {
 
  Waitress(CoffeeMachine coffeeMachine, Toaster toaster) {
    super(coffeeMachine, 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

The waitress is usually the only one who knows how to operate the coffee machine properly.

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 abstract class

Opinions expressed by Java Code Geeks contributors are their own.

Ted Vinke

Ted is a Java software engineer with a passion for Web development and JVM languages and works for First8, a Java Web development company in the Netherlands.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button