简体   繁体   中英

With InjectMocks, one of my mock become null, without it, an other Mock is null

So in this version, repository is null (and metric is not null):

@Mock
OperationRepository repository;
@Mock
Metric metric;

SQLStrategy sqlStrategy;

@BeforeEach
public void setup() {
    MockitoAnnotations.initMocks(this);
    sqlStrategy = new SQLStrategy(metric);
}

In this version, metric is null (and repository is not null):

@Mock
OperationRepository repository;
@Mock
ReneMetric reneMetric;

@BeforeEach
public void setup() {
    MockitoAnnotations.initMocks(this);
}

@InjectMocks
private SQLStrategy sqlStrategy = new SQLStrategy(reneMetric);

This is how they are defined in the class:

@Slf4j
@RequiredArgsConstructor
@Component("sqlDb")
public class SQLStrategy extends StorageStrategy {

@Autowired
private OperationRepository repository;

private final Metric metric;

I was wondering what could be doing this behavior?

I can understand the need for @InjectMocks for repository to not be null since its an @autowired But I really dont understand why metric become null when I use @InjectMocks .

First of all, @InjectMocks is a Mockito stuff and is nothing to do with Spring. It has no ideas about @Autowired and its existence will not affect @InjectMocks 's behaviour.

Normally you do not need to manually create the instance that you want to inject mocks into. If that instance has non no-arg constructor, Mockito will choose biggest constructors to create instance and inject the mocks through this constructor.

But in case the instance has a no-arg constructor or you instantiate it manually, Mockito will first use property setter injection, and then field injection to inject mocks.

And one caveat is that for the final field, field injection will not happen. You can refer to the docs for more information.

So change to the following should inject both mocks into SQLStrategy :

@ExtendWith(MockitoExtension.class)
public class SQLStrategyTest {
    
    @Mock
    OperationRepository repository;

    @Mock
    ReneMetric reneMetric;


    @InjectMocks
    private SQLStrategy sqlStrategy;


    @Test
    public void test(){
    }


}

@Slf4j
@RequiredArgsConstructor
@Component("sqlDb")
public class SQLStrategy extends StorageStrategy {

    public SQLStrategy(OperationRepository repo , Metric metric){
        this.repo = repo;
        this.metric = metric;
    }
}

And the following explains why your example get such result:

  1. You manually instantiate SQLStrategy . But at the moment when it is instantiated, Mockito still not create a ReneMetric mock, so new SQLStrategy(null) is called to create SQLStrategy

  2. Mockito create a mocked instance of OperationRepository and ReneMetric

  3. Since you manually create SQLStrategy , Mockito will try to use property setter injection, and then field injection to inject the mocks into it. For the OperationRepository , it is injected because of the field injection. For the Metric , it is not injected because it is a final field and SQLStrategy does not have any setter for it.

  4. So OperationRepository is not null and Metric is null

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM