1. 簡介
<em @MockBean</em>> 是 Spring 框架提供的註解。它能夠創建一個 Spring Bean 的 mock 對象,允許我們在測試過程中將實際 Bean 替換為 mock 對象。這在集成測試中尤其有用,因為我們希望隔離某些組件,而無需依賴其實際實現。
在本教程中,我們將探討各種配置 <em @MockBean</em>> 組件的方法,以測試 Spring Boot 應用程序。
2. 早期配置的需求
配置 @MockBean 組件在應用程序啓動之前至關重要,當我們需要在測試過程中控制應用程序中某些 Bean 的行為時,尤其是在這些 Bean 與外部系統(如數據庫或 Web 服務)進行交互時。
早期配置的優勢:
- 隔離測試:通過模擬其依賴項,有助於隔離待測試單元的行為
- 避免外部調用:防止原始 Bean 否則會調用外部系統(如數據庫或外部 API)
- 控制 Bean 行為:我們可以預定義模擬 Bean 的響應和行為,從而確保測試具有可預測性,並且不依賴於外部因素
3. 早期配置技術
我們首先將探討如何配置 @MockBean 組件,然後探索在應用程序啓動之前配置這些組件的各種方法。
3.1. 在測試類中直接聲明
這是最簡單的模擬 Bean 的方法。我們可以直接在測試類中的字段上使用 <em/>@MockBean</em/> 註解。 Spring Boot 會自動將實際 Bean 替換為模擬的 Bean,並在上下文中完成替換:
@SpringBootTest(classes = ConfigureMockBeanApplication.class)
public class DirectMockBeanConfigUnitTest {
@MockBean
private UserService mockUserService;
@Autowired
private UserController userController;
@Test
void whenDirectMockBean_thenReturnUserName(){
when(mockUserService.getUserName(1L)).thenReturn("John Doe");
assertEquals("John Doe", userController.getUserName(1L));
verify(mockUserService).getUserName(1L);
}
}在這種方法中,Mock 無縫地替換了 Spring 上下文中真實的 Bean,從而允許依賴該 Bean 的其他 Bean 使用 Mock。 我們可以獨立地定義和控制 Mock,而無需影響其他測試。
3.2. 使用 @MockBean 與 @BeforeEach 進行配置
我們可以使用 Mockito 在 @BeforeEach 方法中配置 mock bean,確保它們在測試開始前可用。
當我們需要在較高層級配置某些 bean,例如 repository、service 或 controller – 這些 bean 不直接可測試或具有複雜的依賴關係時,這非常有用。
在集成測試中,組件通常相互依賴,@MockBean 與 @BeforeEach 幫助創建一個受控環境,通過在每個測試的開始時隔離某些組件並模擬其依賴項,從而使我們能夠專注於正在測試的功能:
@SpringBootTest(classes = ConfigureMockBeanApplication.class)
public class ConfigureBeforeEachTestUnitTest {
@MockBean
private UserService mockUserService;
@Autowired
private UserController userController;
@BeforeEach
void setUp() {
when(mockUserService.getUserName(1L)).thenReturn("John Doe");
}
@Test
void whenParentContextConfigMockBean_thenReturnUserName(){
assertEquals("John Doe", userController.getUserName(1L));
verify(mockUserService).getUserName(1L);
}
}這種方法確保在每次測試之前,我們的模擬配置會被重置,這使其適用於隔離測試。
3.3. 使用 <em @Mockbean</em>> 在嵌套測試配置類中
為了使測試類更清晰,並重用 Mock 配置,我們可以將 Mock 設置移動到一個單獨的嵌套 配置 類中。我們需要使用 <em @Mockbean</em>> 標記該配置類。在該類中,我們實例化並配置 Mock:
@SpringBootTest(classes = ConfigureMockBeanApplication.class)
@Import(InternalConfigMockBeanUnitTest.TestConfig.class)
public class InternalConfigMockBeanUnitTest {
@TestConfiguration
static class TestConfig {
@MockBean
UserService userService;
@PostConstruct
public void initMock(){
when(userService.getUserName(3L)).thenReturn("Bob Johnson");
}
}
@Autowired
private UserService userService;
@Autowired
private UserController userController;
@Test
void whenConfiguredUserService_thenReturnUserName(){
assertEquals("Bob Johnson", userController.getUserName(3L));
verify(userService).getUserName(3L);
}
}這種方法有助於測試類專注於測試,配置分離,從而更容易管理複雜的配置。
3.4. 使用 @Mockbean 在外部測試配置類中
當我們需要在多個測試類之間重用測試配置時,可以將測試配置外部化到一個單獨的類中。 類似於之前的做法,我們需要使用 @TestConfiguration 註解來標記配置類。 這允許我們創建一個專門用於測試的配置類,該類可以用於在 Spring 上下文中模擬或替換 Bean:
@TestConfiguration
class TestConfig {
@MockBean
UserService userService;
@PostConstruct
public void initMock(){
when(userService.getUserName(2L)).thenReturn("Jane Smith");
}
}我們可以在我們的測試類中使用 @Import(TestConfig.class)導入此測試配置:
@SpringBootTest(classes = ConfigureMockBeanApplication.class)
@Import(TestConfig.class)
class ConfigureMockBeanApplicationUnitTest {
@Autowired
private UserService mockUserService;
@Autowired
private UserController userController;
@Test
void whenConfiguredUserService_thenReturnUserName(){
assertEquals("Jane Smith", userController.getUserName(2L));
verify(mockUserService).getUserName(2L);
}
}這種方法在我們需要配置多個測試組件或想要擁有可重用、可在不同測試用例之間共享的mock設置時非常有效。
3.5. 針對特定配置
當我們需要針對不同的配置方案進行測試,例如不同的環境(例如開發或測試環境),我們可以創建針對特定配置方案的配置。通過將 @ActiveProfiles 應用到我們的測試類上,我們可以為我們的測試需求加載不同的應用程序配置。
讓我們為我們的開發配置方案創建一個測試配置:
@Configuration
@Profile("Dev")
class DevProfileTestConfig {
@MockBean
UserService userService;
@PostConstruct
public void initMock(){
when(userService.getUserName(4L)).thenReturn("Alice Brown");
}
}然後,我們可以將活動配置文件設置為“Dev”:
@SpringBootTest(classes = ConfigureMockBeanApplication.class)
@ActiveProfiles("Dev")
public class ProfileBasedMockBeanConfigUnitTest {
@Autowired
private UserService userService;
@Autowired
private UserController userController;
@Test
void whenDevProfileActive_thenReturnUserName(){
assertEquals("Alice Brown", userController.getUserName(4L));
verify(userService).getUserName(4L);
}
}這種方法在不同的環境中(如開發、測試或生產)進行測試時非常有用,可以確保我們模擬了特定用户畫像所需的精確條件。
3.6. 使用 Mockito 的 Answer 實現動態 Mock
當我們需要對 Mock 的行為進行更精細的控制,例如根據運行時輸入或條件動態更改響應時,可以使用 Mockito 的 Answer 接口。 這允許我們在測試開始前配置動態 Mock 的行為:
@SpringBootTest(classes = ConfigureMockBeanApplication.class)
public class MockBeanAnswersUnitTest {
@MockBean
private UserService mockUserService;
@Autowired
private UserController userController;
@BeforeEach
void setUp() {
when(mockUserService.getUserName(anyLong())).thenAnswer(invocation ->{
Long input = invocation.getArgument(0);
if(input == 1L)
return "John Doe";
else if(input == 2L)
return "Jane Smith";
else
return "Bob Johnson";
});
}
@Test
void whenDirectMockBean_thenReturnUserName(){
assertEquals("John Doe", mockUserService.getUserName(1L));
assertEquals("Jane Smith", mockUserService.getUserName(2L));
assertEquals("Bob Johnson", mockUserService.getUserName(3L));
verify(mockUserService).getUserName(1L);
verify(mockUserService).getUserName(2L);
verify(mockUserService).getUserName(3L);
}
}在此示例中,我們根據方法調用配置了動態響應,這使我們在複雜的測試場景中擁有更大的靈活性。
4. 測試策略與注意事項
- 避免過度使用 Mock: 過度使用 @MockBean 可能會降低測試的有效性。理想情況下,我們應將 Mock 的使用限制在那些真正外部或難以控制的依賴項上(例如,外部 API 和數據庫)。
- 使用 @TestConfiguration 進行復雜設置: 當我們需要配置更復雜的行為時,我們應該使用 @TestConfiguration,因為它允許我們更優雅地設置 Mock 並提供更好的高級配置支持。
- 驗證交互: 除了設置返回值之外,驗證與 Mock 的交互也至關重要。 這確保了正確的 메서드가按預期被調用。
5. 結論
配置在應用程序啓動前使用 <em/>@MockBean</em/> 組件可以作為測試 Spring Boot 應用程序的寶貴策略,因為它允許我們對模擬的依賴項進行精細控制。
在本文中,我們學習了各種配置 Mock Bean 組件的方法。通過遵循適合我們測試的方法,並應用最佳實踐和測試策略,我們可以有效地隔離組件並在一個受控環境中驗證其行為。