1. 程式人生 > 其它 >在 Spring 中 Mock RestTemplate

在 Spring 中 Mock RestTemplate

技術標籤:Spring Boot 系列springspring test

如果我們程式中使用了 RestTemplate 進行 HTTP API 呼叫。通常在編寫單元測試時,為了讓測試可控,會將 RestTemlate 呼叫進行 mock,而不是進行真實的 HTTP API 呼叫。

這裡,我們將介紹兩種 mock RestTemplate 呼叫的方法。一個是比較流行的 Mockito 模擬庫,另一個是使用 Spring Test 提供的 MockRestServiceServer 模擬伺服器,它可以建立模擬伺服器以定義伺服器互動。

使用 Mockito 模擬

使用 Mockito 模擬 RestTemplate 測試我們的服務將像其他任何涉及模擬的測試一樣簡單,下面我們就來看看怎麼使用吧。

下面這段程式碼描述的是,一個 UserService 類,該類通過 HTTP 請求獲取使用者資訊。

@Service("userServiceRest")
public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    public List<UserVO> getUsers() {
        UserVO[] users = restTemplate.getForObject("http://localhost:8080/users"
, UserVO[].class); if (users == null) { return Collections.emptyList(); } return Arrays.asList(users); } }

現在,我們要為 UserService 類的 getUsers() 編寫單元測試。測試程式碼如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
    
    // 模擬一個假的 RestTemplate 例項
@Mock private RestTemplate restTemplate; @Autowired @Qualifier("userServiceRest") @InjectMocks private UserService userService; @Before public void setup() { MockitoAnnotations.initMocks(this); } @Test public void testGetUsers() { UserVO mockUser = new UserVO(1, "mock-test"); // 模擬一個假的請求 Mockito.when(restTemplate.getForObject("http://localhost:8080/users", UserVO[].class)) .thenReturn(new UserVO[]{mockUser}); List<UserVO> users = userService.getUsers(); Assert.assertFalse(CollectionUtils.isEmpty(users)); Assert.assertEquals(1, users.size()); UserVO userVO = users.get(0); Assert.assertEquals(mockUser.getId(), userVO.getId()); Assert.assertEquals(mockUser.getName(), userVO.getName()); } }

上面的單元測試中,我們首先使用 Mockito 模擬庫中 @Mock 註解建立一個假 RestTemplate 例項。

然後,我們使用 @InjectMocks 註釋了 UserService 例項,以將模擬的例項注入到其中。

最後,在測試方法中,我們使用 Mockito 的 when() 和 then() 方法定義了模擬的行為。

使用 Spring Test 模擬

Spring Test 模組中包含一個叫 MockRestServiceServer 的模擬伺服器。通過這種方法,我們將伺服器配置為在通過 RestTemplate 例項排程特定請求時返回特定物件。最後,我們可以在該伺服器例項上呼叫 verify() 方法驗證是否滿足所有期望。

MockRestServiceServer 實際上是通過使用 MockClientHttpRequestFactory 攔截 HTTP API 呼叫來工作的。根據我們的配置,它會建立預期請求和相應的響應列表。當 RestTemplate 例項呼叫 API 時,它將在期望列表中查詢請求並返回相應的響應。因此,它無需在任何其他埠上執行 HTTP 伺服器來發送模擬響應。

下面,我們使用 MockRestServiceServer 為 UserService 類的 getUsers() 編寫單元測試,程式碼如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceMockRestServiceServerTest {

    @Autowired
    private UserService userService;

    @Autowired
    private RestTemplate restTemplate;

    private MockRestServiceServer mockServer;

    private ObjectMapper mapper = new ObjectMapper();

    @Before
    public void init() {
        mockServer = MockRestServiceServer.createServer(restTemplate);
    }

    @Test
    public void testGetUsers() throws Exception {
        UserVO mockUser = new UserVO(1, "mock-test");
        // 模擬 RestTemplate 請求
        mockServer.expect(ExpectedCount.once(),
                MockRestRequestMatchers.requestTo(new URI("http://localhost:8080/users")))
                .andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
                .andRespond(MockRestResponseCreators.withStatus(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(mapper.writeValueAsString(new UserVO[]{mockUser}))
                );
        List<UserVO> users = userService.getUsers();
        Assert.assertFalse(CollectionUtils.isEmpty(users));
        UserVO userVO = users.get(0);
        Assert.assertEquals(mockUser.getId(), userVO.getId());
        Assert.assertEquals(mockUser.getName(), userVO.getName());
        mockServer.verify();
    }

}

上面程式碼段中,我們使用 MockRestRequestMatchers 和 MockRestResponseCreators 中的靜態方法以清晰易讀的方式定義 REST 呼叫的期望和響應:

MockRestRequestMatchers.requestTo(new URI("http://localhost:8080/users"));

MockRestRequestMatchers.method(HttpMethod.GET);

MockRestResponseCreators.withStatus(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(mapper.writeValueAsString(new UserVO[]{mockUser});

這裡需要提醒大家的是,測試類中的 RestTemplate 應該與 UserService 類中使用的例項相同。為了確保這一點,我們需要在 spring 容器中定義 RestTemplate bean,並在測試和實現中自動連線例項。

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

當我們編寫整合測試並且只需要模擬外部 HTTP 呼叫時,使用 MockRestServiceServer 非常有用。

最後,最後,小夥伴們可以在 GitHub 中獲取原始碼