Ответ 1
Spring -test имеет гибкий макет запроса, называемый MockHttpServletRequest.
MockHttpServletRequest request = new MockHttpServletRequest();
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
Я пишу unit test для метода, который содержит следующую строку:
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
Я получаю следующую ошибку:
java.lang.IllegalStateException: не найдено ни одного запроса, связанного с потоком: Are вы ссылаетесь на атрибуты запроса вне фактического веб-запроса, или обработки запроса за пределами первоначально принимающего потока? Если вы фактически работаете в веб-запросе и все еще получаете это сообщение, ваш код, вероятно, работает за пределами DispatcherServlet/DispatcherPortlet: В этом случае используйте RequestContextListener или RequestContextFilter для отображения текущей запрос.
Причина совершенно очевидна - я не запускаю тест в контексте запроса.
Вопрос в том, как я могу проверить метод, содержащий вызов метода, зависящего от контекста запроса в тестовой среде?
Большое спасибо.
Spring -test имеет гибкий макет запроса, называемый MockHttpServletRequest.
MockHttpServletRequest request = new MockHttpServletRequest();
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
Вы можете высмеять/заглушить RequestAttributes
объект, чтобы вернуть то, что вы хотите, а затем вызвать RequestContextHolder.setRequestAttributes(RequestAttributes)
с вашим макетом/заглушкой перед началом теста.
@Mock
private RequestAttributes attrs;
@Before
public void before() {
MockitoAnnotations.initMocks(this);
RequestContextHolder.setRequestAttributes(attrs);
// do you when on attrs
}
@Test
public void testIt() {
// do your test...
}
Предполагая, что ваш класс похож на:
class ClassToTest {
public void doSomething() {
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
// Do something with sessionId
}
}
Если у вас нет возможности изменить класс, который использует RequestContextHolder
, вы можете переопределить класс RequestContextHolder
в тестовом коде.
То есть вы создаете класс с тем же именем в том же пакете и убедитесь, что он загружен до фактического класса Spring.
package org.springframework.web.context.request;
public class RequestContextHolder {
static RequestAttributes currentRequestAttributes() {
return new MyRequestAttributes();
}
static class MyRequestAttributes implements RequestAttributes {
public String getSessionId() {
return "stub session id";
}
// Stub out the other methods.
}
}
Теперь, когда ваши тесты будут запущены, они подберут ваш класс RequestContextHolder
и используют это, предпочитая Spring один (при условии, что для этого будет создан путь к классам).
Это не очень хороший способ запуска ваших тестов, но может потребоваться, если вы не можете изменить тестируемый класс.
В качестве альтернативы вы можете скрыть извлечение идентификатора сеанса за абстракцией. Например, введите интерфейс:
public interface SessionIdAccessor {
public String getSessionId();
}
Создайте реализацию:
public class RequestContextHolderSessionIdAccessor implements SessionIdAccessor {
public String getSessionId() {
return RequestContextHolder.currentRequestAttributes().getSessionId();
}
}
И используйте абстракцию в своем классе:
class ClassToTest {
SessionIdAccessor sessionIdAccessor;
public ClassToTest(SessionIdAccessor sessionIdAccessor) {
this.sessionIdAccessor = sessionIdAccessor;
}
public void doSomething() {
String sessionId = sessionIdAccessor.getSessionId();
// Do something with sessionId
}
}
Затем вы можете предоставить фиктивную реализацию для своих тестов:
public class DummySessionIdAccessor implements SessionIdAccessor {
public String getSessionId() {
return "dummy session id";
}
}
Подобные вещи подчеркивают обычную передовую практику, чтобы скрыть некоторые экологические детали за абстракциями, чтобы вы могли их заменить, если ваша среда изменилась. Это в равной степени относится к тому, чтобы ваши тесты были менее хрупкими, заменяя фиктивные реализации для "реальных".
Если метод содержит:
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
- это метод веб-контроллера, тогда я бы рекомендовал изменить сигнатуру метода, чтобы вы / spring передали этот запрос в качестве отдельного paremter для этого метода.
Затем вы можете удалить часть нарушителя String RequestContextHolder.currentRequestAttributes()
и использовать HttpSession
прямо.
Тогда в тесте должно быть очень просто использовать объект с надстроенным сеансом (MockHttpSession
).
@RequestMapping...
public ModelAndView(... HttpSession session) {
String id = session.getId();
...
}
Я смог сделать то же самое, что и этот ответ, но без класса MockHttpServletRequest
используя аннотацию @Mock
. Я думаю, они похожи. Просто разместив здесь сообщения для будущих посетителей.
@Mock
HttpServletRequest request;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
}