In our projects we define a java.time.Clock Bean for UTC times, which we use everywhere for getting the now time.

Instant.now(clockUtc)

In test cases we mock this clock like

 @MockBean
 private Clock clockUtc;

@Before
public void setUp() {
    when(clockUtc.instant()).thenReturn(Instant.parse("2020-03-03T12:00:00.000Z"));
}

This worked fine for our use cases. But since we updated to Spring Boot 2.4 we stumpeled upon a problem:

When the clockUtc is called at an ApplicationReadyEvent it's mocked, but it's stubbing from the setUp() wasn't called and therefore clockUtc.instant() return null instead of "2020-03-03T12:00:00.000Z".

This behaviour is differs from Spring Boot 2.2. there the setup() was called before the ApplicationReadyEvent was handled.

As a workaround we created a @TestConfiguration which creates the mock with the stubbing.

This is a change of behaviour of @Before, is this a known issue, a bug or did i miss something in the patch notes?

Edit: Only happens if used inside a rxpipeline not when called "normally"

Comment From: wilkinsona

I'm surprised that this worked for you with Spring Boot 2.2. The application context should be fully refreshed before any @Before or @Test methods on the test class are called. This means that you can't use @MockBean and @Before to configure expectations. This is described in the documentation:

@MockBean cannot be used to mock the behavior of a bean that’s exercised during application context refresh. By the time the test is executed, the application context refresh has completed and it is too late to configure the mocked behavior. We recommend using a @Bean method to create and configure the mock in this situation.

In short, using @TestConfiguration and a @Bean method to create the mock, as you have been doing, is the recommended approach.

With that said, it would be interesting to understand the cause of the change in behaviour. To that end, could you please share a minimal sample with us that works with Spring Boot 2.2 and fails with 2.4?

Comment From: Farix1337

While writing an example i discovered that my described case will not reproduce the problem. Instead i need to create a rxpipeline which calls the mock at startup. So it works just as you expected @wilkinsona but with rx something changed. Im working at an example.