Насмешка с помощью ContentCachingResponseWrapper для проверки фильтра

#java #spring-boot

Вопрос:

Я реализовал фильтр, который считывает тело ответа и подписывает его. Он использует обработчик ответов ContentCachingResponseWrapper:

 @Slf4j
public class ResponseSignerFilter extends AbstractSigner implements Filter {


    public ResponseSignerFilter(....) {
//..constructor...
    }

 @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        final ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(httpServletRequest);

        final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        final ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(httpServletResponse);

        chain.doFilter(requestWrapper, responseWrapper);

       final boolean responseHasBody = responseWrapper.getContentSize() > 0;

 if (responseHasBody) {
//use body to sign:
     byte[] body = responseWrapper.getContentAsByteArray()
//...more code for the signing...
            }

        responseWrapper.copyBodyToResponse();
  }

//init amp; destroy override methods
}
 

Я хочу протестировать этот фильтр с помощью простого теста mockito. Однако издевательство не работает, так как оболочка ContentCachingRequestWrapper останется пустой. Я попробовал что-то вроде этого:

 @ExtendWith(MockitoExtension.class)
class ResponseSignerFilterTest {


  @Test
    void exampleTest() throws ServletException, IOException {
        final ResponseSignerFilter filter = new ResponseSignerFilter(appPrivateKey, getJwtSettings());

        final MockHttpServletRequest req = new MockHttpServletRequest();
        req.setMethod("POST");

        final MockHttpServletResponse res = new MockHttpServletResponse();
        res.setContentType("application/json");

        final byte[] someBodyBytes = "give some body".getBytes(StandardCharsets.UTF_8);

        final MockFilterChain filterChain = new MockFilterChain(new HttpServlet() {
            @Override
            protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
                res.getWriter().write("test-body");
                //alternative, does same thing:
                // res.getOutputStream().write("test-body".getBytes(StandardCharsets.UTF_8));
                res.setContentLength(5);
                res.setCommitted(true);
            }
        });

        filter.doFilter(req, res, filterChain);
        
        //assertions on res
    }
}
 

Цепочка mockfilter действительно работает в том смысле, что она записывает ответ, но на обертке написано «пусто»!

Фактическая реализация фильтра работает, поэтому я знаю, что этот обработчик содержимого работает отлично (если я позвоню почтальону в приложение с помощью этого реализованного фильтра). Но в простом тесте mockito он терпит неудачу. Поток fastbytearrayoutput в оболочке остается пустым. Как я могу написать ответ таким образом, чтобы его подхватила оболочка? Я не могу найти, как оболочка извлекает содержимое из ответа при выполнении chain.dofilter.

Заранее спасибо за любую помощь!

Стог

Ответ №1:

Что я обнаружил, что сработало для меня, так это расширение метода doFilter в классе MockFilterChain. Вот пример, который заполнит ваш ответ и обработчик содержимого.

 private class FakeContentFilterChain extends MockFilterChain {

 private byte[] responseContent;

 FakeContentFilterChain(byte[] fakeContentToInject) {
  this.responseContent = fakeContentToInject;
 }

 // Need to short-circuit the filter call chain to fake populate the response
 @Override
 public void doFilter(ServletRequest arg0, ServletResponse arg1)
    throws IOException, ServletException {
  if (arg1 instanceof HttpServletResponse) {
    final HttpServletResponse httpServletResponse = (HttpServletResponse) arg1;
    httpServletResponse.setStatus(HttpStatus.OK.value());
    httpServletResponse.getOutputStream().write(responseContent);
  }
 }
}
 

Затем вы можете использовать его в своем коде как:

 FilterChain filterChain = new FakeContentFilterChain("test-body".getBytes());
filter.doFilter(req, res, filterChain);
 

Комментарии:

1. Это хорошая идея, которую я еще не опробовал. Я попробую это сделать, может быть, позже на этой неделе. Мне пришлось двигаться дальше без решения, так как у меня некоторое время не было ответа, но я рассмотрю его, когда у меня будет для этого время.

2. Классно! Держите нас в курсе

3. Наконец-то у меня было время еще раз взглянуть на этот проект. И да, ваше решение сработало идеально! Большое спасибо.