Spring Boot 3.4へ上げたら… — here’s the English translation of the article body:


Many developers upgrading to Spring Boot 3.4 have likely noticed that @MockBean, which they had been using as a matter of course, is now struck through and triggers a deprecation warning. Since it’s an annotation that has been used all over test articles, being suddenly treated as deprecated can be confusing.

To get straight to the point, the replacements are @MockitoBean and @MockitoSpyBean. In this article, we’ll walk through the correct imports and package changes, the differences from the old API, and a procedure for migrating your existing tests mechanically all at once.

Why @MockBean Was Deprecated in Spring Boot 3.4

@MockBean and @SpyBean became @Deprecated in Spring Boot 3.4. The reason is simple: the mock injection mechanism, which had previously been a Boot-specific implementation, has been integrated into a standard mechanism called Bean Override that was introduced in Spring Framework 6.2.

In other words, what used to be a “Boot-specific convenience feature” has, along with its role, moved to a “Framework-standard testing feature.” Bean Override is a general-purpose mechanism that can also be used to swap in things other than Mockito, and the Mockito-oriented annotations are provided as one part of it.

The important thing is that it has merely been deprecated — it still works. Your existing tests won’t turn red the moment you upgrade to 3.4. Only a warning is shown, so there’s no need to panic; you can migrate in a planned manner.

Mapping of Replacement Annotations and Packages

Let’s first nail down what gets replaced with what. The key point is that not only the annotation name but also the import package changes.

Old (Deprecated)New
@MockBean@MockitoBean
@SpyBean@MockitoSpyBean

The packages change as follows.

// Old: on the Spring Boot side
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;

// New: on the Spring Framework side
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;

The package has moved entirely from boot.test.mock.mockito to test.context.bean.override.mockito. If you only change the annotation name, the import won’t resolve and you’ll get a compile error, so you need to fix both together.

Basic Usage of @MockitoBean

Let’s look at the typical pattern of testing a controller with @WebMvcTest and mocking the Service it depends on. First, the old code.

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    UserService userService;

    @Test
    void getUser() throws Exception {
        when(userService.findById(1L))
            .thenReturn(new User(1L, "Alice"));

        mockMvc.perform(get("/users/1"))
            .andExpect(status().isOk());
    }
}

When you replace this with @MockitoBean, the only things that change are the annotation and the import.

import org.springframework.test.context.bean.override.mockito.MockitoBean;

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockitoBean
    UserService userService;

    // The way you write when(...) and verify(...) stays the same
}

What I’d like you to notice is that the Mockito-side syntax such as when and verify doesn’t change at all. Only the entry-point annotation that swaps out the Bean has changed, so the logic of the test body itself works as is. The way MockMvc tests themselves are written is covered in How to Test Controllers with Spring Boot’s MockMvc, so take a look there too.

Using @MockitoSpyBean

For a spy — that is, when you want to call the actual implementation while stubbing only part of it — you would have been using @SpyBean. Replace this with @MockitoSpyBean in the same manner.

import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;

@SpringBootTest
class OrderServiceTest {

    @MockitoSpyBean
    NotificationService notificationService;

    @Test
    void placeOrder() {
        // Call the implementation while replacing only some methods
        doNothing().when(notificationService).notify(any());
        // ...
    }
}

The partial-mock behavior — namely, that methods other than the ones you explicitly stub are called on the actual implementation as is — is also preserved. When using it in other slices such as @DataJpaTest, the approach is the same: just swap out the annotation and the import. For details on slice testing itself, refer to How to Slice-Test Repositories with @DataJpaTest.

Differences from @MockBean and Migration Caveats

Most cases can be handled by simply replacing the annotation name, but you’ll have peace of mind if you keep the Bean-resolution rules in mind.

@MockitoBean resolves the target Bean using the field’s type. If there is only one Bean of the same type, it gets swapped out as is, but when there are multiple candidates of the same type, it becomes ambiguous which one to replace. In that case, specify the target explicitly with the name attribute.

@MockitoBean(name = "primaryUserService")
UserService userService;

These resolution rules aren’t very different from the old @MockBean way of thinking, but when migrating a test that has multiple candidates, it’s safer to confirm that the intended target is actually being swapped out after the replacement. For subtle version-dependent behavioral differences, we recommend checking the latest specification in the official reference rather than assuming.

Procedure for Migrating Existing Tests All at Once

When you have dozens of test classes, fixing them by hand isn’t realistic. Let’s find them with grep and replace them in bulk with sed. First, let’s check the target locations.

grep -rn "MockBean\|SpyBean" src/test/java

Once you’ve grasped the count and locations, replace the imports and annotation names in bulk. In terms of order, it’s safer to process the longer package import lines first.

# Replace import lines first
grep -rl "org.springframework.boot.test.mock.mockito.SpyBean" src/test/java \
  | xargs sed -i 's#org.springframework.boot.test.mock.mockito.SpyBean#org.springframework.test.context.bean.override.mockito.MockitoSpyBean#g'

grep -rl "org.springframework.boot.test.mock.mockito.MockBean" src/test/java \
  | xargs sed -i 's#org.springframework.boot.test.mock.mockito.MockBean#org.springframework.test.context.bean.override.mockito.MockitoBean#g'

# Replace annotation names (SpyBean first)
grep -rl "@SpyBean" src/test/java | xargs sed -i 's/@SpyBean/@MockitoSpyBean/g'
grep -rl "@MockBean" src/test/java | xargs sed -i 's/@MockBean/@MockitoBean/g'

The reason @SpyBean is processed before @MockBean is to prevent Spy from being caught up in the simple replacement. Once the replacement is done, compile and check that no unused imports remain. Using your IDE’s import-organizing feature makes this easy.

Finally, be sure to run the tests and confirm they’re green. Since this is a mechanical replacement, it will basically pass, but the multiple-candidate case mentioned earlier is the one where behavior may change, so you can catch it here.

./gradlew test

What Happens If You Leave It Deprecated

You might think, “If it works, isn’t it fine to just leave it?” but a deprecated API may be removed in a future major version. If you upgrade at that point, this time it will hit you all at once as a compile error rather than a warning.

Also, if a large number of deprecation warnings keep appearing, they become noise in the build log, and the warnings you really should be looking at get buried. It quietly accumulates as technical debt, so it’s not a particularly pleasant state to be in.

Realistically, we recommend migrating everything in bulk as part of your Spring Boot upgrade work. The grep/sed procedure introduced here finishes in a few minutes, and it also serves as a good opportunity to review how your tests are written overall. For Spring Boot 2-to-3 migration in general, it’s covered in The Spring Boot 2 to 3 Upgrade Guide, so plan it out together.

Summary

The deprecation of @MockBean is a natural progression of a Boot-specific feature being integrated into the Framework-standard Bean Override. What you need to do is simple: just swap the annotation and import to @MockitoBean / @MockitoSpyBean. The Mockito syntax itself doesn’t change. Migrate everything in bulk with grep and sed, confirm your tests are green, and you’re done. Let’s take care of it quickly while it’s still just a warning.