Anton Belev
Anton Belev

Reputation: 13523

How to inject mocked object into another already mocked object

I have a class that I mocked like this:

@Mock
private MyClass1 mockMyClass1;

Say this class looks like that:

public class MyClass1 {
  @Autowired
  private MyInnerClass myInnerClass;
}

When I mock MyClass1, MyInnerClass will be initialized as null. Is it possible to initialize that private field with another mock objected? I want myInnerClass to be a mock like this one:

@Mock
private MyInnerClass myInnerClass;

If I use the following code:

@InjectMocks
private MyClass1 mockMyClass1;
@Mock
private MyInnerClass myInnerClass

This will initialize the private MyInnerClass inside MyClass1 to be the mocked object, however this way mockMyClass1 is not a mock itself any more, isn't it (I'm only injecting a mocked object inside a real class, just like @Autowired will inject a real object insite it)? Could you correct my logic if I'm wrong in my understanding of @InjectMock and @Mock? Also how could I inject a mocked object inside another mocked object (without using setters/constructors)?

Upvotes: 4

Views: 9451

Answers (2)

Tunaki
Tunaki

Reputation: 137064

You are misunderstanding what a mock is.

When you are mocking MyClass1, the inner field of type MyInnerClass doesn't exist anymore for the mocked object. As such, it doesn't make sense to try to inject something into a mock.

A mock is controlled by saying what it should do when you interact with it. You stub its public methods to do what you want them to do. You give behaviour. The only default behaviour (maybe specific to Mockito) is to return null if you didn't configure the mock (but do note that it doesn't return null because some internal variable is null, it returns that because Mockito decided that that's what it should return; maybe another mocking framework decided that it should throw an exception instead, who knows?).

As an example, consider that your real class is:

class MyClass1 {
    private MyInnerClass myInnerClass;
    public MyInnerClass getMyInnerClass() {
        return myInnerClass;
    }
}

Now consider mock a mock of that class. If you call mock.getMyInnerClass(), it is not the real method that is going to be called. What it will return is not the value of myInnerClass. It will return null, or if you have stubbed that method with

when(mock.getMyInnerClass()).thenReturn(something);

then it will return something.

So to answer your question: you can't do that because that's not how mocks work.

Upvotes: 9

Mateusz Dryzek
Mateusz Dryzek

Reputation: 661

You shouldn't really use InjectMocks annotation at all (to see why, you can read this article).

You could just implement an Autowired constructor for MyClass1 instead of Autowired fields. Then you could simply pass your MyInnerClass mock reference as a constructor parameter.

Upvotes: 1

Related Questions