我使用Mockito和JUnit来测试应用程序。我需要在嘲笑时向HttpServletRequest
添加标题。这是我第一次使用mock概念来测试应用程序。在使用这个mock概念的同时,我们如何设置请求对象的头?
应用程序代码
@Produces({ MediaType.APPLICATION_JSON })
@Path("/devices")
public class DvrRestService {
private static final Logger logger = LoggerFactory.getLogger(DvrRestService.class);
private DvrMiddleService dvrMiddleService;
@Inject
public DvrRestService(DvrMiddleService dvrMiddleService) {
this.dvrMiddleService = dvrMiddleService;
}
@GET
@Path("/{deviceId}/metadata")
public Response getDeviceMetadata(@Context HttpServletRequest request, @PathParam("deviceId") String deviceId,
@RequiredSession final Session session) {
try {
public static String[] REQUEST_HEADERS = { "if-none-match" };
List<String> requiredHeaders = Lists.newArrayList(REQUEST_HEADERS);
Map<String, String> headers = new HashMap<String, String>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) { // here gettting NullPointerException
String headerName = headerNames.nextElement();
if (requiredHeaders.contains(headerName.toLowerCase())) {
String value = request.getHeader(headerName);
if (value != null) {
headers.put(headerName, value);
System.out.println("headerName: " + headerName + ", Value: " + value);
}
}
}
DvrResponse response = dvrMiddleService.getDeviceMetadata(deviceId.toUpperCase(), getHeaders(request));
return processResponse(response.statusCode, response.getResponse(), DeviceMetadataResponse.class,
response.getHeaders());
} catch (Exception e) {
return processErrorResponse(e, new DeviceMetadataResponse(), logger);
}
}
}
测试
public class DvrRestServiceTest {
static DvrMiddleService dms;
static HttpServletRequest request;
static Session session;
static DvrRestService drs;
public static final String DeviceId = "000004D42070";
@BeforeClass
public static void init(){
dms = mock(DvrMiddleService.class);
request = mock(HttpServletRequest.class);
session = mock(Session.class);
drs = new DvrRestService(dms);
}
@Test
public void getDeviceMetadataTest(){
Response rs = drs.getDeviceMetadata(request, DeviceId, session);
assertEquals(Response.Status.OK, rs.getStatus());
}
}
作为主体的起点和演示,您可以从以下片段开始。
// define the headers you want to be returned
Map<String, String> headers = new HashMap<>();
headers.put(null, "HTTP/1.1 200 OK");
headers.put("Content-Type", "text/html");
// create an Enumeration over the header keys
Enumeration<String> headerNames = Collections.enumeration(headers.keySet());
// mock HttpServletRequest
HttpServletRequest request = mock(HttpServletRequest.class);
// mock the returned value of request.getHeaderNames()
when(request.getHeaderNames()).thenReturn(headerNames);
System.out.println("demonstrate output of request.getHeaderNames()");
while (headerNames.hasMoreElements()) {
System.out.println("header name: " + headerNames.nextElement());
}
// mock the returned value of request.getHeader(String name)
doAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
return headers.get((String) args[0]);
}
}).when(request).getHeader("Content-Type");
System.out.println("demonstrate output of request.getHeader(String name)");
String headerName = "Content-Type";
System.out.printf("header name: [%s] value: [%s]%n",
headerName, request.getHeader(headerName));
}
输出
demonstrate output of request.getHeaderNames()
header name: null
header name: Content-Type
demonstrate output of request.getHeader(String name)
header name: [Content-Type] value: [text/html]
对于HttpServletRequest
,我建议使用功能齐全的mock类型,而不是直接用Mockito mock来嘲笑它。spring-test
库具有用于此目的的MockHttpServletRequest
:
@BeforeClass
public static void init(){
// ...
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
mockRequest.addHeader("Content-Type", "text/html");
mockRequest.addHeader("if-none-match", "*");
mockRequest.addHeader("customHeader", "customValue");
this.request = mockRequest;
}
基本原理
HttpServletRequest
是一个有20多种方法的复杂接口,它们之间有明确的相互作用。从库中为HttpServletRequest
使用一个功能齐全的mock类型可以简化mock,从而无需仔细地对所使用的方法进行mock。
这种方法的一个优点是,面对未来使用类上的其他方法获得相同信息的重构,它更有弹性。在检索";如果没有匹配"0";在HttpServletRequest
中,我看到三种不同的方法可以合法地用于检索标头:getHeader(String name)
、getHeaders(String name)
和getHeaderNames()
。此外,getHeader
和getHeaders
的自变量不区分大小写(对于"ifnonematch"、"ifNonematch"等返回相同的结果),因此任何可能的自变量大小写都是正确的。通过直接mock支持这一点是非常可能的,尽管它涉及额外的样板代码,这会使测试变得复杂,并使其不那么明显。
spring-test
库中的MockHttpServletRequest
类模拟此接口,并允许通过简单的API设置头和其他值。虽然该库是为测试Spring应用程序而设计的,但MockHttpServletRequest
类独立于任何Spring特定的功能,即使应用程序不使用Spring,也应该完全可以单独使用。
这在我的案例中起到了作用
mockMvc.perform(post("<<url>>").content("<<jsonStrig>>").header("key", "value"));
也可以用于get请求。
我知道OP正在使用Mockito。这个答案适用于那些使用spock的人。你可以很容易地做到这一点。
class MyTestSpec extends Specification {
HttpServletRequest request = Mock()
MyTestClass myTestClass = new MyTestClass()
def 'my test'() {
setup:
String expectedHeader = "x-mycustom-header"
when:
String someResult = myTestClass.someTestMethod()
then:
// here is where you return your header from the mocked request
1 * request.getHeader(_ as String) >> expectedHeader
}
}