Тестирование проекта с использованием Guice Servlet: как связать экземпляры RequestScoped вне области действия?
У нас есть веб-приложение на Java, которое создается с использованием Guice и расширения Guice Servlet. Приложение также включает в себя задания Quartz, которые также создаются Guice. Я хочу написать модульный тест для этих заданий.
Классы заданий зависят от других классов нашего производительного кода, которые требуют Provider<HttpServletRequest>
, В продуктивной настройке задания могут быть успешно созданы Guice, а задания работают как положено, потому что они никогда не вызывают какой-либо код на своих соавторах, который вызывает get
на запросе сервлета провайдера. Такой get
вызов завершится с ошибкой, потому что задания выполняются некоторым рабочим потоком, а не как часть HTTP-запроса к сервлету.
Теперь мой вопрос заключается в том, как настроить Guice в тесте JUnit так, чтобы я получал то же поведение, то есть классы заданий могут быть созданы, но все попытки использовать какие-либо вне области видимости @RequestScoped
объекты потерпят неудачу.
Есть два простых решения, но они не работают для меня.
Если я не буду связывать HttpServletRequest
Я получаю сообщение об ошибке при попытке создать экземпляр задания в настройках теста:
com.google.inject.CreationException: Guice creation errors:
1) No implementation for javax.servlet.http.HttpServletRequest was bound.
while locating com.google.inject.Provider<javax.servlet.http.HttpServletRequest>
Если, с другой стороны, я просто связываю фиктивные экземпляры, например, с
@Override
protected void configure() {
bind(HttpServletRequest.class).toInstance(mock(HttpServletRequest.class));
}
тогда задание может быть создано, но я больше не получаю тестовую ошибку, если задание использует экземпляр запроса сервлета. Итак, как я могу создать привязку, чтобы Guice мог создавать экземпляры поставщиков, но любое использование поставщиков потерпит неудачу?
1 ответ
После нескольких попыток я узнал, как связать @RequestScoped
объекты в тестовом модуле:
@Override
protected void configure() {
bindScope(RequestScoped.class, ServletScopes.REQUEST);
bind(ServletRequest.class).toProvider(unusableProvider(ServletRequest.class)).in(RequestScoped.class);
bind(HttpServletRequest.class).toProvider(unusableProvider(HttpServletRequest.class)).in(RequestScoped.class);
bind(ServletResponse.class).toProvider(unusableProvider(ServletResponse.class)).in(RequestScoped.class);
bind(HttpServletResponse.class).toProvider(unusableProvider(HttpServletResponse.class)).in(RequestScoped.class);
}
private static <T> Provider<T> unusableProvider(final Class<T> type) {
return new Provider<T>() {
@Override
public T get() {
throw new IllegalStateException("Unexpected call to provider of " + type.getSimpleName());
}
};
}
Тесты никогда не входят в область запроса (т.е. нет вызовов ServletScope.scopeRequest
), поэтому все попытки получить RequestScoped
объекты в тестах приведут к точно такой же ошибке, как и в производстве:
com.google.inject.ProvisionException: Guice provision errors:
1) Error in custom provider, com.google.inject.OutOfScopeException: Cannot access scoped object. Either we are not currently inside an HTTP Servlet request, or you may have forgotten to apply com.google.inject.servlet.GuiceFilter as a servlet filter for this request.
Связывание с "непригодным провайдером" на самом деле не нужно - я просто сделал это в качестве дополнительной системы безопасности. Это означает, что это решение также должно работать для классов, которые явно не связаны, но которые помечены @RequestScoped
и автоматически обнаруживается Guice.