关联 ID 的单元测试 doFilter
Unit Testing doFilter for Correlation Id
我正在使用 dropwizard 构建应用程序。
我制作了一个过滤器来拦截和记录调用服务的相关 ID。
如果传入请求的 header 中没有 header "Correlation-Id",我们会在响应中附加一个。
过滤条件如下:
public class CorrelationIdServletFilter implements Filter {
private static final Logger LOGGER =
LoggerFactory.getLogger(CorrelationIdServletFilter.class);
private static final String CORRELATION_ID_HEADER_NAME = "Correlation-ID";
private static final String CORRELATION_ID_MDC_KEY = " ";
private static final InheritableThreadLocal<String> correlationId =
new InheritableThreadLocal<>();
public static String getCorrelationId() {
return correlationId.get();
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String correlationIdHeaderValue = req.getHeader(CORRELATION_ID_HEADER_NAME);
LOGGER.debug
(
"HTTP Header("
+ CORRELATION_ID_HEADER_NAME
+ ") = ["
+ correlationIdHeaderValue
+ "] will generate a new correlationId if incoming is NULL");
String correlationIdRaw;
if (!StringUtils.isEmpty(correlationIdHeaderValue)) {
correlationIdRaw = correlationIdHeaderValue;
} else {
correlationIdRaw = UUID.randomUUID().toString();
}
LOGGER.debug("Request: (" + req.getRequestURI() + ") is marked as :" + correlationIdRaw);
correlationId.set(correlationIdRaw);
MDC.put(CORRELATION_ID_MDC_KEY, getCorrelationId());
res.addHeader(CORRELATION_ID_HEADER_NAME, correlationIdRaw);
LOGGER.debug(
"Response holds correlationId : ("
+ res.getHeader("Correlation-ID")
+ ") in its header ");
chain.doFilter(req, res);
} finally {
correlationId.remove();
MDC.remove(CORRELATION_ID_MDC_KEY);
}
}
@Override
public void destroy() {}
}
我需要编写涵盖两种情况的单元测试:
发送没有关联 ID 的请求时。检查在服务器端生成的 id。
发送带有关联 ID 的响应时。检查是否已通过响应发回。
谁能告诉我如何做到这一点?
我试过使用 mock,但我没有响应 header。
@Test
public void testResponse_for_RequestWithoutCcid() throws IOException, ServletException {
HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
FilterChain filterChain = mock(FilterChain.class);
CorrelationIdServletFilter CorrelationIdServletFilter = mock(
CorrelationIdServletFilter.class);
CorrelationIdServletFilter.init(mock(FilterConfig.class));
CorrelationIdServletFilter.doFilter(httpServletRequest, httpServletResponse,
filterChain);
System.out.println(httpServletResponse.getHeaderNames());
CorrelationIdServletFilter.destroy();
verify(CorrelationIdServletFilter, times(1))
.doFilter(httpServletRequest, httpServletResponse, filterChain);
}
有什么办法可以做到这一点吗?任何帮助将非常感激。有没有办法不用模拟就可以做到这一点?
您编写的测试的一些主要问题:
- 被测试的 class 永远不会被模拟(可能会有一些例外),因为您想对被测试的 class 的各种方法进行实际调用以进行单元测试。
- 我们应该始终为被测 class 的不同方法编写单独的单元测试。在这里我可以看到您还调用了
init
和 destroy
方法,当您想要测试 doFilter
方法时不需要这些方法。
- 当我们创建任何模拟对象时,我们使用期望来定义我们期望对模拟对象进行的调用,并在需要时为它们return一些存根值。
现在,我已经尝试编写正确的测试来断言您要测试的两种情况:
@Test
public void testResponse_for_RequestWithoutCcid() throws IOException, ServletException {
HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
FilterChain filterChain = mock(FilterChain.class);
CorrelationIdServletFilter correlationIdServletFilter = new CorrelationIdServletFilter();
expect(httpServletRequest.getHeader(CORRELATION_ID_HEADER_NAME)).andReturn(""); // Empty correlation id in the request
Capture capturedCorrelationIdRaw = newCapture();
httpServletResponse.addHeader(CORRELATION_ID_HEADER_NAME, capture(capturedCorrelationIdRaw));
expectLastCall(); // used for void methods in EasyMock framework
filterChain.doFilter(httpServletRequest, httpServletResponse);
expectLastCall();
CorrelationIdServletFilter.doFilter(httpServletRequest, httpServletResponse,
filterChain);
assertNotEmpty(capturedCorrelationIdRaw.getValue());
verify(httpServletRequest, times(1))
.getHeader(CORRELATION_ID_HEADER_NAME);
verify(httpServletResponse, times(1))
.addHeader(CORRELATION_ID_HEADER_NAME, anyString);
}
此测试需要根据实际使用的测试框架进行更新,但我已尽力让您了解测试应该是什么样子。
我正在使用 dropwizard 构建应用程序。
我制作了一个过滤器来拦截和记录调用服务的相关 ID。
如果传入请求的 header 中没有 header "Correlation-Id",我们会在响应中附加一个。
过滤条件如下:
public class CorrelationIdServletFilter implements Filter {
private static final Logger LOGGER =
LoggerFactory.getLogger(CorrelationIdServletFilter.class);
private static final String CORRELATION_ID_HEADER_NAME = "Correlation-ID";
private static final String CORRELATION_ID_MDC_KEY = " ";
private static final InheritableThreadLocal<String> correlationId =
new InheritableThreadLocal<>();
public static String getCorrelationId() {
return correlationId.get();
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String correlationIdHeaderValue = req.getHeader(CORRELATION_ID_HEADER_NAME);
LOGGER.debug
(
"HTTP Header("
+ CORRELATION_ID_HEADER_NAME
+ ") = ["
+ correlationIdHeaderValue
+ "] will generate a new correlationId if incoming is NULL");
String correlationIdRaw;
if (!StringUtils.isEmpty(correlationIdHeaderValue)) {
correlationIdRaw = correlationIdHeaderValue;
} else {
correlationIdRaw = UUID.randomUUID().toString();
}
LOGGER.debug("Request: (" + req.getRequestURI() + ") is marked as :" + correlationIdRaw);
correlationId.set(correlationIdRaw);
MDC.put(CORRELATION_ID_MDC_KEY, getCorrelationId());
res.addHeader(CORRELATION_ID_HEADER_NAME, correlationIdRaw);
LOGGER.debug(
"Response holds correlationId : ("
+ res.getHeader("Correlation-ID")
+ ") in its header ");
chain.doFilter(req, res);
} finally {
correlationId.remove();
MDC.remove(CORRELATION_ID_MDC_KEY);
}
}
@Override
public void destroy() {}
}
我需要编写涵盖两种情况的单元测试:
发送没有关联 ID 的请求时。检查在服务器端生成的 id。
发送带有关联 ID 的响应时。检查是否已通过响应发回。
谁能告诉我如何做到这一点?
我试过使用 mock,但我没有响应 header。
@Test
public void testResponse_for_RequestWithoutCcid() throws IOException, ServletException {
HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
FilterChain filterChain = mock(FilterChain.class);
CorrelationIdServletFilter CorrelationIdServletFilter = mock(
CorrelationIdServletFilter.class);
CorrelationIdServletFilter.init(mock(FilterConfig.class));
CorrelationIdServletFilter.doFilter(httpServletRequest, httpServletResponse,
filterChain);
System.out.println(httpServletResponse.getHeaderNames());
CorrelationIdServletFilter.destroy();
verify(CorrelationIdServletFilter, times(1))
.doFilter(httpServletRequest, httpServletResponse, filterChain);
}
有什么办法可以做到这一点吗?任何帮助将非常感激。有没有办法不用模拟就可以做到这一点?
您编写的测试的一些主要问题:
- 被测试的 class 永远不会被模拟(可能会有一些例外),因为您想对被测试的 class 的各种方法进行实际调用以进行单元测试。
- 我们应该始终为被测 class 的不同方法编写单独的单元测试。在这里我可以看到您还调用了
init
和destroy
方法,当您想要测试doFilter
方法时不需要这些方法。 - 当我们创建任何模拟对象时,我们使用期望来定义我们期望对模拟对象进行的调用,并在需要时为它们return一些存根值。
现在,我已经尝试编写正确的测试来断言您要测试的两种情况:
@Test
public void testResponse_for_RequestWithoutCcid() throws IOException, ServletException {
HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
FilterChain filterChain = mock(FilterChain.class);
CorrelationIdServletFilter correlationIdServletFilter = new CorrelationIdServletFilter();
expect(httpServletRequest.getHeader(CORRELATION_ID_HEADER_NAME)).andReturn(""); // Empty correlation id in the request
Capture capturedCorrelationIdRaw = newCapture();
httpServletResponse.addHeader(CORRELATION_ID_HEADER_NAME, capture(capturedCorrelationIdRaw));
expectLastCall(); // used for void methods in EasyMock framework
filterChain.doFilter(httpServletRequest, httpServletResponse);
expectLastCall();
CorrelationIdServletFilter.doFilter(httpServletRequest, httpServletResponse,
filterChain);
assertNotEmpty(capturedCorrelationIdRaw.getValue());
verify(httpServletRequest, times(1))
.getHeader(CORRELATION_ID_HEADER_NAME);
verify(httpServletResponse, times(1))
.addHeader(CORRELATION_ID_HEADER_NAME, anyString);
}
此测试需要根据实际使用的测试框架进行更新,但我已尽力让您了解测试应该是什么样子。