如何模擬 HttpServletRequest
一、概述
在這個快速教程中,我們將了解一些模擬HttpServletRequest
對象的方法。
首先,我們將從一個功能齊全的模擬類型開始——來自 Spring Test 庫的MockHttpServletRequest
。然後,我們將看到如何使用兩個流行的模擬庫進行測試——Mockito 和 JMockit。最後,我們將看到如何使用匿名子類進行測試。
2.測試HttpServletRequest
當我們想要模擬諸如HttpServletRequest
之類的客戶端請求信息時,測試 Servlet 可能會很棘手。此外,這個接口定義了各種方法,並且有不同的方法可以模擬這些方法。
讓我們看看我們要測試的目標UserServlet
類:
public class UserServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String firstName = request.getParameter("firstName");
String lastName = request.getParameter("lastName");
response.getWriter().append("Full Name: " + firstName + " " + lastName);
}
}
要對doGet()
方法進行單元測試,我們需要模擬request
和response
參數來模擬實際的運行時行為。
3. 使用 Spring 中的MockHttpServletRequest
Spring-Test庫提供了一個功能齊全的類[MockHttpServletRequest](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/mock/web/MockHttpServletRequest.html)
,它實現了HttpServletRequest
接口。
雖然這個庫主要用於測試 Spring 應用程序,但我們可以使用它的MockHttpServletRequest
類而不實現任何 Spring 特定的功能。換句話說,即使應用程序不使用 Spring,我們仍然可以使用這種依賴項來模擬HttpServletRequest
對象.
讓我們將此依賴項添加到 pom.xml:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.20</version>
<scope>test</scope>
</dependency>
現在,讓我們看看如何使用這個類來測試UserServlet
:
@Test
void givenHttpServletRequest_whenUsingMockHttpServletRequest_thenReturnsParameterValues() throws IOException {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("firstName", "Spring");
request.setParameter("lastName", "Test");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.doGet(request, response);
assertThat(response.getContentAsString()).isEqualTo("Full Name: Spring Test");
}
在這裡,我們可以注意到沒有涉及實際的模擬。我們使用了功能齊全的請求和響應對象,並僅用幾行代碼測試了目標類。因此,測試代碼是乾淨的、可讀的和可維護的。
4. 使用模擬框架
或者,模擬框架提供了一個乾淨簡單的 API 來測試模擬原始對象的運行時行為的模擬對象。
它們的一些優點是它們的可表達性和開箱即用的模擬static
和private
方法的能力。此外,我們可以避免模擬所需的大部分樣板代碼(與自定義實現相比),而是專注於測試。
4.1。使用 Mockito
Mockito 是一個流行的開源測試自動化框架,它在內部使用 Java 反射 API 來創建模擬對象。
讓我們開始將 mockito-core依賴項添加到我們的pom.xml
中:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
接下來,讓我們看看如何從HttpServletRequest
對像中模擬getParameter()
方法:
@Test
void givenHttpServletRequest_whenMockedWithMockito_thenReturnsParameterValues() throws IOException {
// mock HttpServletRequest & HttpServletResponse
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
// mock the returned value of request.getParameterMap()
when(request.getParameter("firstName")).thenReturn("Mockito");
when(request.getParameter("lastName")).thenReturn("Test");
when(response.getWriter()).thenReturn(new PrintWriter(writer));
servlet.doGet(request, response);
assertThat(writer.toString()).isEqualTo("Full Name: Mockito Test");
}
4.2.使用 JMockit
JMockit 是一個模擬 API,它提供了有用的記錄和驗證語法(我們可以將它用於JUnit和TestNG )。它是用於 Java EE 和基於 Spring 的應用程序的容器外集成測試庫。讓我們看看如何使用 JMockit 模擬HttpServletRequest
。
首先,我們將jmockit
依賴添加到我們的項目中:
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.49</version>
<scope>test</scope>
</dependency>
接下來,讓我們繼續測試類中的模擬實現:
@Mocked
HttpServletRequest mockRequest;
@Mocked
HttpServletResponse mockResponse;
@Test
void givenHttpServletRequest_whenMockedWithJMockit_thenReturnsParameterValues() throws IOException {
new Expectations() {{
mockRequest.getParameter("firstName"); result = "JMockit";
mockRequest.getParameter("lastName"); result = "Test";
mockResponse.getWriter(); result = new PrintWriter(writer);
}};
servlet.doGet(mockRequest, mockResponse);
assertThat(writer.toString()).isEqualTo("Full Name: JMockit Test");
}
正如我們在上面看到的,只需要幾行設置,我們就已經成功地用一個模擬的HttpServletRequest
對象測試了目標類。
因此,模擬框架可以為我們節省大量的跑腿工作,並使單元測試的編寫速度更快。相反,要使用模擬對象,需要了解模擬 API,而且通常需要單獨的框架。
5. 使用匿名子類
一些項目可能有依賴約束或者更喜歡直接控制他們自己的測試類實現。具體來說,這在自定義實現的可重用性很重要的較大 servlet 代碼庫的情況下可能很有用。在這些情況下,匿名類就派上用場了。
匿名類是沒有名字的內部類。此外,它們可以快速實施並提供對實際對象的直接控制。如果我們不想為測試包含額外的依賴項,可以考慮這種方法。
現在,讓我們創建一個實現HttpServletRequest
接口的匿名子類,並用它來測試doGet()
方法:
public static HttpServletRequest getRequest(Map<String, String[]> params) {
return new HttpServletRequest() {
public Map<String, String[]> getParameterMap() {
return params;
}
public String getParameter(String name) {
String[] values = params.get(name);
if (values == null || values.length == 0) {
return null;
}
return values[0];
}
// More methods to implement
}
};
接下來,讓我們將此請求傳遞給被測類:
@Test
void givenHttpServletRequest_whenUsingAnonymousClass_thenReturnsParameterValues() throws IOException {
final Map<String, String[]> params = new HashMap<>();
params.put("firstName", new String[] { "Anonymous Class" });
params.put("lastName", new String[] { "Test" });
servlet.doGet(getRequest(params), getResponse(writer));
assertThat(writer.toString()).isEqualTo("Full Name: Anonymous Class Test");
}
該解決方案的缺點是需要為所有抽象方法創建一個具有虛擬實現的匿名類。此外,**像HttpSession
這樣的嵌套對象可能需要特定的實現**。
六,結論
在本文中,我們討論了在為 servlet 編寫單元測試時HttpServletRequest
對象的一些選項。除了使用模擬框架外,我們還看到使用MockHttpServletRequest
類進行測試似乎比自定義實現更乾淨、更高效。
與往常一樣,這些示例的代碼可在 GitHub 上找到。