0

I have some beans defined in a spring boot project, BeanA:

@Component
public class BeanA {
    public String getName() {
        return "A";
    }
}

and BeanB:

@Component
public class BeanB {
    @Autowired
    private BeanA beanA;

    private String name;

    @PostConstruct
    public void init() {
        this.name = beanA.getName();
    }

    public String getName() {
        return this.name;
    }

}

when I mock the beanA.getName, it return null

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { MockitoTestApplication.class })
public class BeanTest {
  @Autowired
  private BeanB beanB;

  @MockBean
  private BeanA beanA;

  @Before
  public void mock() {
    MockitoAnnotations.initMocks(this);
    Mockito.doReturn("mockA").when(beanA).getName();
  }

  @Test
  public void testGetName() {
    System.out.println(beanB.getName()); // return null here
  }
}

I guess there is some bean load priority here, what's the root cause and how to fix it?

mckszcz
  • 144
  • 8
fudy
  • 1,540
  • 5
  • 25
  • 41
  • 2
    Your mock behavior is regsitered **after** the beans have already been constructed and thus `getName` is already being called. Rewrite your `getName` to do `return beanA.getName()` and it will return what you want. Also remove the `MockitoAnntations.initMocks` Spring Boot Test support handles that for you . – M. Deinum Nov 05 '20 at 09:29

1 Answers1

0

First off you don't need to use MockitoAnnotations.initMocks(this); @MockBean creates a mock and adds the bean to the application context (adds a new one or substitutes the existing one). Technically this mock is already "mockito powered" so you can specify expectations on it, etc.

Now the real problem with the code is that you're trying to access the beans in the @Before method that technically is called before spring does its magic, its a part of JUnit framework.

For cases like this spring testing library have invented the concept of listeners which are bound to the spring application context lifecycle as opposed to the lifecycle of the unit test.

So technically you may call Mockito.doReturn("mockA").when(beanA).getName(); from within the listener TestExecutionListener#beforeTestMethod will do the job.

For general explanations read this SO thread and more in-depth with examples this article

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • the beanA will be null in when() , if I add Mock logic in beforeTestMethod – fudy Nov 05 '20 at 11:42
  • Can you share the code please? Are you using the correct merge mode? `@TestExecutionListeners(value = {MyFilter.class}, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)` – Mark Bramnik Nov 05 '20 at 11:45
  • Btw, just to clarify: the comment you've got from @M.Deinum is totally correct so you should consider this answer in addition to that comment. – Mark Bramnik Nov 05 '20 at 13:56