I'm writing a unit test for a method that contains the following line:
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
I get the following error:
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
The reason is quite obvious — I'm not running the test in a request context.
The question is, how can I test a method that contains a call to a method dependent to the request context in a test environnment?
Thank you very much.
I was able to do the same as this answer, but without the
MockHttpServletRequest
class using the@Mock
annotation. I guess they're similar. Just posting here for future visitors.You can mock/stub the
RequestAttributes
object to return what you want, and then callRequestContextHolder.setRequestAttributes(RequestAttributes)
with your mock/stub before you start your test.Assuming your class is something like:
If you don't have the ability to change the class that uses
RequestContextHolder
, then you could override theRequestContextHolder
class in your test code. I.e. you create a class with the same name, in the same package, and ensure it is loaded before the actual Spring class.Now, when your tests run, they will pick up your
RequestContextHolder
class and use that in preference to the Spring one (assuming the classpath is set up for this to happen). This isn't a particular nice way of getting your tests to run, but it might be necessary if you can't change the class you are testing.Alternatively, you could hide the session id retrieval behind an abstraction. For example introduce an interface:
Create an implementation:
And use the abstraction in your class:
Then you can provide a dummy implementation for your tests:
This sort of thing highlights a usual best-practice to hide certain environmental details behind abstractions so that you can swap them out if your environment changes. This applies equally to making your tests less brittle by swapping dummy implementations for 'real' ones.
Spring-test has a flexible request mock called MockHttpServletRequest.
If the method containing:
is the web controller method, then I would recommend to change the method signature, so that you/spring pass the Request as an seperate paremter to the method.
Then you can remove the troublemaker part String
RequestContextHolder.currentRequestAttributes()
and use theHttpSession
direcly.Then it should be very easy to use a mocked Session (
MockHttpSession
) object in the test.