在應用程式啟動之前配置@MockBean元件
一、簡介
@MockBean
是Spring框架提供的註解。它有助於創建 Spring bean 的模擬對象,允許我們在測試期間用模擬替換實際的 bean。這在整合測試期間特別有用,因為我們希望隔離某些元件而不依賴它們的實際實作。
在本教程中,我們將研究配置@MockBean
元件以測試 Spring Boot 應用程式的各種方法。
2. 早期配置的必要性
當我們需要在測試期間控制應用程式中某些 Bean 的行為時,在應用程式啟動之前配置@MockBean
元件至關重要,特別是當這些 Bean 與資料庫或 Web 服務等外部系統互動時。
早期配置的好處:
- 隔離測試:它透過模擬被測單元的依賴關係來幫助隔離被測單元的行為
- 避免外部調用:它阻止對外部系統的調用,例如資料庫或外部 API,否則這些系統將由原始 bean 調用
- 控制bean行為:我們可以預先定義模擬bean的反應和行為,這確保測試是可預測的並且不依賴外部因素
3. 早期配置技巧
我們將首先檢查如何配置@MockBean
元件,然後探索在應用程式啟動之前配置這些元件的各種方法。
3.1.在測試類別中直接聲明
這是模擬 bean 最簡單的方法。我們可以直接在測試類別中的欄位上使用@MockBean
註解。 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);
}
}
在這種方法中,模擬無縫地替換了 Spring 上下文中的真實 bean,從而允許依賴它的其他 bean 也可以使用模擬。我們可以獨立定義和控制模擬,而不影響其他測試。
3.2.使用@BeforeEach
配置@MockBean
我們可以在@BeforeEach
方法中使用Mockito
配置模擬 bean,確保它們在測試開始之前可用。
當我們想要在更高層級模擬某些 bean(例如儲存庫、服務或控制器)時,它非常有用——這些 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.在嵌套測試配置類別中使用@Mockbean
為了使測試類別更清晰並重複使用模擬配置,我們可以將模擬設定移至單獨的巢狀配置類別中。我們需要使用@TestConfiguration
註解來配置類別。在此類中,我們實例化並配置模擬:
@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);
}
}
當我們需要配置多個測試元件或想要擁有可以在不同測試案例之間共用的可重複使用模擬設定時,這種方法非常有用。
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
當我們想要更多地控制模擬行為時,例如在運行時根據輸入或其他條件動態更改響應,我們可以利用 Mockito 的Answer
介面。這允許我們在測試開始之前配置動態模擬行為:
@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. 測試策略和注意事項
- 避免過多的模擬:過度使用
@MockBean
會降低我們測試的有效性。理想情況下,我們應該將模擬的使用限制在那些真正外部或難以控制的依賴項上(例如,外部 API 和資料庫)。 - 使用
@TestConfiguration
進行複雜的設定:當我們需要配置更複雜的行為時,我們應該使用@TestConfiguration
,因為這允許我們更優雅地設定模擬並為高級配置提供更好的支援。 - 驗證交互作用:除了設定回傳值之外,驗證與模擬的交互作用也很重要。這確保了按預期調用正確的方法。
5. 結論
在應用程式啟動之前配置@MockBean
元件可能是測試 Spring Boot 應用程式的一個有價值的策略,因為這使我們能夠微調對模擬依賴項的控制。
在本文中,我們學習了配置模擬 bean 元件的各種方法。透過遵循測試所需的方法並應用最佳實踐和測試策略,我們可以有效地隔離組件並驗證受控環境中的行為。
與往常一樣,本文中使用的所有程式碼片段都可以在 GitHub 上找到。